Example #1
0
        /// <summary>
        /// Create a new schema resolver in the context of the given swagger spec
        /// </summary>
        /// <param name="modeler">The swagger spec modeler</param>
        public SchemaResolver(SwaggerModeler modeler)
        {
            if (modeler == null)
            {
                throw new ArgumentNullException("modeler");
            }

            _serviceDefinition = modeler.ServiceDefinition;
        }
Example #2
0
 /// <summary>
 /// Create a new schema resolver in the context of the given swagger spec
 /// </summary>
 /// <param name="modeler">The swagger spec modeler</param>
 public SchemaResolver(ServiceDefinition definition)
 {
     _serviceDefinition = definition;
 }
Example #3
0
        public override ServiceClient Build(out IEnumerable<ValidationMessage> messages)
        {
            Logger.LogInfo(Resources.ParsingSwagger);
            if (string.IsNullOrWhiteSpace(Settings.Input))
            {
                throw ErrorManager.CreateError(Resources.InputRequired);
            }
            ServiceDefinition = SwaggerParser.Load(Settings.Input, Settings.FileSystem);

            // Look for semantic errors and warnings in the document.
            var validator = new RecursiveObjectValidator(PropertyNameResolver.JsonName);
            messages = validator.GetValidationExceptions(ServiceDefinition).ToList();

            Logger.LogInfo(Resources.GeneratingClient);
            // Update settings
            UpdateSettings();

            InitializeClientModel();
            BuildCompositeTypes();

            // Build client parameters
            foreach (var swaggerParameter in ServiceDefinition.Parameters.Values)
            {
                var parameter = ((ParameterBuilder)swaggerParameter.GetBuilder(this)).Build();

                var clientProperty = new Property();
                clientProperty.LoadFrom(parameter);

                ServiceClient.Properties.Add(clientProperty);
            }

            // Build methods
            foreach (var path in ServiceDefinition.Paths.Concat(ServiceDefinition.CustomPaths))
            {
                foreach (var verb in path.Value.Keys)
                {
                    var operation = path.Value[verb];
                    if (string.IsNullOrWhiteSpace(operation.OperationId))
                    {
                        throw ErrorManager.CreateError(
                            string.Format(CultureInfo.InvariantCulture,
                                Resources.OperationIdMissing,
                                verb,
                                path.Key));
                    }
                    var methodName = GetMethodName(operation);
                    var methodGroup = GetMethodGroup(operation);

                    if (verb.ToHttpMethod() != HttpMethod.Options)
                    {
                        string url = path.Key;
                        if (url.Contains("?"))
                        {
                            url = url.Substring(0, url.IndexOf('?'));
                        }
                        var method = BuildMethod(verb.ToHttpMethod(), url, methodName, operation);
                        method.Group = methodGroup;
                        ServiceClient.Methods.Add(method);
                        if (method.DefaultResponse.Body is CompositeType)
                        {
                            ServiceClient.ErrorTypes.Add((CompositeType)method.DefaultResponse.Body);
                        }
                    }
                    else
                    {
                        Logger.LogWarning(Resources.OptionsNotSupported);
                    }
                }
            }

            // Set base type
            foreach (var typeName in GeneratedTypes.Keys)
            {
                var objectType = GeneratedTypes[typeName];
                if (ExtendedTypes.ContainsKey(typeName))
                {
                    objectType.BaseModelType = GeneratedTypes[ExtendedTypes[typeName]];
                }

                ServiceClient.ModelTypes.Add(objectType);
            }

            return ServiceClient;
        }
Example #4
0
        /// <summary>
        /// In order to avoid comparing definitions (schemas) that are not used, we go through all references that are
        /// found in operations, global parameters, and global responses. Definitions that are referenced from other
        /// definitions are included only by transitive closure.
        /// </summary>
        private static void ReferenceTrackSchemas(ServiceDefinition service)
        {
            foreach (var schema in service.Definitions.Values)
            {
                schema.IsReferenced = false;
            }

            foreach (var path in service.Paths.Values)
            {
                foreach (var operation in path.Values)
                {
                    foreach (var parameter in operation.Parameters)
                    {
                        if (parameter.Schema != null && !string.IsNullOrWhiteSpace(parameter.Schema.Reference))
                        {
                            var schema = FindReferencedSchema(parameter.Schema.Reference, service.Definitions);
                            schema.IsReferenced = true;
                        }
                    }
                }
            }
            foreach (var path in service.CustomPaths.Values)
            {
                foreach (var operation in path.Values)
                {
                    foreach (var parameter in operation.Parameters)
                    {
                        if (parameter.Schema != null && !string.IsNullOrWhiteSpace(parameter.Schema.Reference))
                        {
                            var schema = FindReferencedSchema(parameter.Schema.Reference, service.Definitions);
                            schema.IsReferenced = true;
                        }
                    }
                }
            }
            foreach (var parameter in service.Parameters.Values)
            {
                if (parameter.Schema != null && !string.IsNullOrWhiteSpace(parameter.Schema.Reference))
                {
                    var schema = FindReferencedSchema(parameter.Schema.Reference, service.Definitions);
                    schema.IsReferenced = true;
                }
            }
            foreach (var response in service.Responses.Values)
            {
                if (response.Schema != null && !string.IsNullOrWhiteSpace(response.Schema.Reference))
                {
                    var schema = FindReferencedSchema(response.Schema.Reference, service.Definitions);
                    schema.IsReferenced = true;
                }
            }

            var changed = true;

            while (changed)
            {
                changed = false;

                foreach (var schema in service.Definitions.Values.Where(d => d.IsReferenced))
                {
                    // If schema does not have properties then do not recurse
                    if (schema.Properties == null)
                    {
                        continue;
                    }

                    foreach (var property in schema.Properties.Values)
                    {
                        if (!string.IsNullOrWhiteSpace(property.Reference))
                        {
                            var s = FindReferencedSchema(property.Reference, service.Definitions);
                            changed        = changed || !s.IsReferenced;
                            s.IsReferenced = true;
                        }
                    }
                }
            }
        }
        /// <summary>
        /// In order to avoid comparing definitions (schemas) that are not used, we go through all references that are
        /// found in operations, global parameters, and global responses. Definitions that are referenced from other
        /// definitions are included only by transitive closure.
        /// </summary>
        private static void ReferenceTrackSchemas(ServiceDefinition service)
        {
            foreach (var schema in service.Definitions.Values)
            {
                schema.IsReferenced = false;
            }

            foreach (var path in service.Paths.Values)
            {
                foreach (var operation in path.Values)
                {
                    foreach (var parameter in operation.Parameters)
                    {
                        if (parameter.Schema != null && !string.IsNullOrWhiteSpace(parameter.Schema.Reference))
                        {
                            var schema = FindReferencedSchema(parameter.Schema.Reference, service.Definitions);
                            schema.IsReferenced = true;
                        }
                    }
                }
            }
            foreach (var path in service.CustomPaths.Values)
            {
                foreach (var operation in path.Values)
                {
                    foreach (var parameter in operation.Parameters)
                    {
                        if (parameter.Schema != null && !string.IsNullOrWhiteSpace(parameter.Schema.Reference))
                        {
                            var schema = FindReferencedSchema(parameter.Schema.Reference, service.Definitions);
                            schema.IsReferenced = true;
                        }
                    }
                }
            }
            foreach (var parameter in service.Parameters.Values)
            {
                if (parameter.Schema != null && !string.IsNullOrWhiteSpace(parameter.Schema.Reference))
                {
                    var schema = FindReferencedSchema(parameter.Schema.Reference, service.Definitions);
                    schema.IsReferenced = true;
                }
            }
            foreach (var response in service.Responses.Values)
            {
                if (response.Schema != null && !string.IsNullOrWhiteSpace(response.Schema.Reference))
                {
                    var schema = FindReferencedSchema(response.Schema.Reference, service.Definitions);
                    schema.IsReferenced = true;
                }
            }

            var changed = true;

            while (changed)
            {
                changed = false;

                foreach (var schema in service.Definitions.Values.Where(d => d.IsReferenced))
                {
                    // If schema does not have properties then do not recurse
                    if (schema.Properties == null)
                    {
                        continue;
                    }

                    foreach (var property in schema.Properties.Values)
                    {
                        if (!string.IsNullOrWhiteSpace(property.Reference))
                        {
                            var s = FindReferencedSchema(property.Reference, service.Definitions);
                            changed        = changed || !s.IsReferenced;
                            s.IsReferenced = true;
                        }
                    }
                }
            }

            // detect the polymorphic concrecate model.
            foreach (var schema in service.Definitions.Values.Where(d => !d.IsReferenced))
            {
                if (schema.Extensions != null && schema.Extensions.ContainsKey("x-ms-discriminator-value"))
                {
                    schema.IsReferenced = true;
                    continue;
                }

                if (schema.AllOf == null)
                {
                    continue;
                }

                foreach (var property in schema.AllOf)
                {
                    if (!string.IsNullOrWhiteSpace(property.Reference))
                    {
                        if (FindDiscriminator(property.Reference, service.Definitions))
                        {
                            schema.IsReferenced = true;
                            break;
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Compare a modified document node (this) to a previous one and look for breaking as well as non-breaking changes.
        /// </summary>
        /// <param name="context">The modified document context.</param>
        /// <param name="previous">The original document model.</param>
        /// <returns>A list of messages from the comparison.</returns>
        public override IEnumerable <ComparisonMessage> Compare(
            ComparisonContext <ServiceDefinition> context,
            ServiceDefinition previousDefinition
            )
        {
            if (context.CurrentRoot != this)
            {
                throw new ArgumentException("context.CurrentRoot != this");
            }
            if (context.PreviousRoot != previousDefinition)
            {
                throw new ArgumentException("context.PreviousRoot != previousDefinition");
            }

            if (previousDefinition == null)
            {
                throw new ArgumentException("Comparing a service definition with something else.");
            }

            base.Compare(context, previousDefinition);

            if (Info?.Version != null &&
                previousDefinition.Info?.Version != null)
            {
                context.PushProperty("info");
                context.PushProperty("version");

                CompareVersions(context, Info.Version, previousDefinition.Info.Version);

                context.Pop();
                context.Pop();
            }

            if (context.Strict)
            {
                // There was no version change between the documents. This is not an error, but noteworthy.
                context.LogInfo(ComparisonMessages.NoVersionChange);
            }

            // Check that all the protocols of the old version are supported by the new version.

            context.PushProperty("schemes");
            foreach (var scheme in previousDefinition.Schemes)
            {
                if (!Schemes.Contains(scheme))
                {
                    context.LogBreakingChange(ComparisonMessages.ProtocolNoLongerSupported, scheme);
                }
            }
            context.Pop();

            // Check that all the request body formats that were accepted still are.

            context.PushProperty("consumes");
            foreach (var format in previousDefinition.Consumes)
            {
                if (!Consumes.Contains(format))
                {
                    context.LogBreakingChange(ComparisonMessages.RequestBodyFormatNoLongerSupported, format);
                }
            }
            context.Pop();

            // Check that all the response body formats were also supported by the old version.

            context.PushProperty("produces");
            foreach (var format in Produces)
            {
                if (!previousDefinition.Produces.Contains(format))
                {
                    context.LogBreakingChange(ComparisonMessages.ResponseBodyFormatNowSupported, format);
                }
            }
            context.Pop();

            // Check that no paths were removed, and compare the paths that are still there.

            var newPaths = RemovePathVariables(Paths);

            context.PushProperty("paths");
            foreach (var path in previousDefinition.Paths.Keys)
            {
                var p = ObjectPath.OpenApiPathName(path);

                context.PushPathProperty(path);

                if (!newPaths.TryGetValue(p, out var operations))
                {
                    // Entrie path was removeed
                    context.LogBreakingChange(ComparisonMessages.RemovedPath, path);
                }
                else
                {
                    // 1. Remove this path from the current list to find the added paths
                    newPaths.Remove(p);
                    var copyOfOperations = operations.ToDictionary(e => e.Key, e => e.Value);

                    // 2. look for operation match inside this path
                    var previousOperations = previousDefinition.Paths[path];
                    foreach (var previousOperation in previousOperations)
                    {
                        if (!operations.TryGetValue(previousOperation.Key, out var newOperation))
                        {
                            // Operation was removed from the path
                            context.LogBreakingChange(ComparisonMessages.RemovedOperation, previousOperation.Value.OperationId);
                        }
                        else
                        {
                            copyOfOperations.Remove(previousOperation.Key);
                        }
                    }

                    // Look for added operations
                    foreach (var copyOfOperation in copyOfOperations)
                    {
                        context.PushProperty(copyOfOperation.Key);
                        context.LogInfo(ComparisonMessages.AddedOperation);
                        context.Pop();
                    }

                    // Compare operations
                    foreach (var operation in operations)
                    {
                        if (previousDefinition.Paths[path].TryGetValue(operation.Key, out var previousOperation))
                        {
                            context.PushProperty(operation.Key);
                            operation.Value.Compare(context, previousOperation);
                            context.Pop();
                        }
                    }
                }
                context.Pop();
            }

            // Check wether any new paths are being added
            foreach (var path in newPaths.Keys)
            {
                context.PushPathProperty(path);
                context.LogInfo(ComparisonMessages.AddedPath);
                context.Pop();
            }


            context.Pop();

            // Check for custom paths : x-ms-paths
            var newCustomPaths = RemovePathVariables(CustomPaths);

            context.PushProperty("x-ms-paths");
            foreach (var path in previousDefinition.CustomPaths.Keys)
            {
                var p = ObjectPath.OpenApiPathName(path);

                context.PushPathProperty(path);

                Dictionary <string, Operation> operations = null;
                if (!newCustomPaths.TryGetValue(p, out operations))
                {
                    context.LogBreakingChange(ComparisonMessages.RemovedPath, path);
                }
                else
                {
                    // 1. Remove this custom path from the current list to find the added paths
                    newCustomPaths.Remove(p);
                    Dictionary <string, Operation> copyOfOperations = operations.ToDictionary(e => e.Key, e => e.Value);

                    // 2. look for operation match inside this path
                    Dictionary <string, Operation> previousOperations = previousDefinition.CustomPaths[path];
                    foreach (var previousOperation in previousOperations)
                    {
                        Operation newOperation = null;
                        if (!operations.TryGetValue(previousOperation.Key, out newOperation))
                        {
                            context.LogBreakingChange(ComparisonMessages.RemovedOperation, previousOperation.Value.OperationId);
                        }
                    }

                    // Look for added operations
                    foreach (var copyOfOperation in copyOfOperations)
                    {
                        context.PushProperty(copyOfOperation.Key);
                        context.LogInfo(ComparisonMessages.AddedOperation);
                        context.Pop();
                    }

                    // Compare operations
                    foreach (var operation in operations)
                    {
                        Operation previousOperation = null;
                        if (previousDefinition.CustomPaths[path].TryGetValue(operation.Key, out previousOperation))
                        {
                            context.PushProperty(operation.Key);
                            operation.Value.Compare(context, previousOperation);
                            context.Pop();
                        }
                    }
                }
                context.Pop();
            }

            // Check wether any new paths are being added into x-ms-paths
            foreach (var path in newCustomPaths.Keys)
            {
                context.PushPathProperty(path);
                context.LogInfo(ComparisonMessages.AddedPath);
                context.Pop();
            }

            context.Pop();

            ReferenceTrackSchemas(this);
            ReferenceTrackSchemas(previousDefinition);

            context.PushProperty("parameters");
            foreach (var def in previousDefinition.Parameters.Keys)
            {
                SwaggerParameter parameter = null;
                if (!Parameters.TryGetValue(def, out parameter))
                {
                    context.LogBreakingChange(ComparisonMessages.RemovedClientParameter, def);
                }
                else
                {
                    context.PushProperty(def);
                    parameter.Compare(context, previousDefinition.Parameters[def]);
                    context.Pop();
                }
            }
            context.Pop();

            context.PushProperty("responses");
            foreach (var def in previousDefinition.Responses.Keys)
            {
                OperationResponse response = null;
                if (!Responses.TryGetValue(def, out response))
                {
                    context.LogBreakingChange(ComparisonMessages.RemovedDefinition, def);
                }
                else
                {
                    context.PushProperty(def);
                    response.Compare(context, previousDefinition.Responses[def]);
                    context.Pop();
                }
            }
            context.Pop();

            context.PushProperty("definitions");
            foreach (var def in previousDefinition.Definitions.Keys)
            {
                Schema schema    = null;
                Schema oldSchema = previousDefinition.Definitions[def];

                if (!Definitions.TryGetValue(def, out schema))
                {
                    if (oldSchema.IsReferenced)
                    {
                        // It's only an error if the definition is referenced in the old service.
                        context.LogBreakingChange(ComparisonMessages.RemovedDefinition, def);
                    }
                }
                else
                {
                    context.PushProperty(def);
                    schema.Compare(context, previousDefinition.Definitions[def]);
                    context.Pop();
                }
            }
            context.Pop();

            context.Pop();

            return(context.Messages);
        }
Example #7
0
        /// <summary>
        /// In order to avoid comparing definitions (schemas) that are not used, we go through all references that are 
        /// found in operations, global parameters, and global responses. Definitions that are referenced from other
        /// definitions are included only by transitive closure.
        /// </summary>
        private static void ReferenceTrackSchemas(ServiceDefinition service)
        {
            foreach (var schema in service.Definitions.Values)
            {
                schema.IsReferenced = false;
            }

            foreach (var path in service.Paths.Values)
            {
                foreach (var operation in path.Values)
                {
                    foreach (var parameter in operation.Parameters)
                    {
                        if (parameter.Schema != null && !string.IsNullOrWhiteSpace(parameter.Schema.Reference))
                        {
                            var schema = FindReferencedSchema(parameter.Schema.Reference, service.Definitions);
                            schema.IsReferenced = true;
                        }
                    }
                }
            }
            foreach (var path in service.CustomPaths.Values)
            {
                foreach (var operation in path.Values)
                {
                    foreach (var parameter in operation.Parameters)
                    {
                        if (parameter.Schema != null && !string.IsNullOrWhiteSpace(parameter.Schema.Reference))
                        {
                            var schema = FindReferencedSchema(parameter.Schema.Reference, service.Definitions);
                            schema.IsReferenced = true;
                        }
                    }
                }
            }
            foreach (var parameter in service.Parameters.Values)
            {
                if (parameter.Schema != null && !string.IsNullOrWhiteSpace(parameter.Schema.Reference))
                {
                    var schema = FindReferencedSchema(parameter.Schema.Reference, service.Definitions);
                    schema.IsReferenced = true;
                }
            }
            foreach (var response in service.Responses.Values)
            {
                if (response.Schema != null && !string.IsNullOrWhiteSpace(response.Schema.Reference))
                {
                    var schema = FindReferencedSchema(response.Schema.Reference, service.Definitions);
                    schema.IsReferenced = true;
                }
            }

            var changed = true;
            while (changed)
            {
                changed = false;

                foreach (var schema in service.Definitions.Values.Where(d => d.IsReferenced))
                {
                    foreach (var property in schema.Properties.Values)
                    {
                        if (!string.IsNullOrWhiteSpace(property.Reference))
                        {
                            var s = FindReferencedSchema(property.Reference, service.Definitions);
                            changed = changed || !s.IsReferenced;
                            s.IsReferenced = true;
                        }
                    }
                }
            }
        }