private void TryBuildDefaultResponse(string methodName, OperationResponse response, Method method, IModelType headerType) { IModelType errorModel = null; if (SwaggerOperationProducesJson()) { if (TryBuildResponseBody(methodName, response, s => GenerateErrorModelName(s), out errorModel)) { method.DefaultResponse = new Response(errorModel, headerType); } } }
private bool TryBuildResponseBody(string methodName, OperationResponse response, Func<string, string> typeNamer, out IModelType responseType) { bool handled = false; responseType = null; if (SwaggerOperationProducesJson()) { if (response.Schema != null) { string referenceKey; if (response.Schema.Reference != null) { referenceKey = response.Schema.Reference.StripDefinitionPath(); response.Schema.Reference = referenceKey; } else { referenceKey = typeNamer(methodName); } responseType = response.Schema.GetBuilder(_swaggerModeler).BuildServiceType(referenceKey); handled = true; } } return handled; }
private bool TryBuildEmptyResponse(string methodName, HttpStatusCode responseStatusCode, OperationResponse response, Method method, List<Stack<IModelType>> types, IModelType headerType) { bool handled = false; if (response.Schema == null) { method.Responses[responseStatusCode] = new Response(null, headerType); handled = true; } else { if (_operation.Produces.IsNullOrEmpty()) { method.Responses[responseStatusCode] = new Response(New<PrimaryType>(KnownPrimaryType.Object), headerType); BuildMethodReturnTypeStack(New<PrimaryType>(KnownPrimaryType.Object), types); handled = true; } var unwrapedSchemaProperties = _swaggerModeler.Resolver.Unwrap(response.Schema).Properties; if (unwrapedSchemaProperties != null && unwrapedSchemaProperties.Any()) { Logger.LogWarning(Resources.NoProduceOperationWithBody, methodName); } } return handled; }
private bool TryBuildResponse(string methodName, HttpStatusCode responseStatusCode, OperationResponse response, Method method, List<Stack<IModelType>> types, IModelType headerType) { bool handled = false; IModelType serviceType; if (SwaggerOperationProducesJson()) { if (TryBuildResponseBody(methodName, response, s => GenerateResponseObjectName(s, responseStatusCode), out serviceType)) { method.Responses[responseStatusCode] = new Response(serviceType, headerType); BuildMethodReturnTypeStack(serviceType, types); handled = true; } } return handled; }
private bool TryBuildStreamResponse(HttpStatusCode responseStatusCode, OperationResponse response, Method method, List<Stack<IModelType>> types, IModelType headerType) { bool handled = false; if (SwaggerOperationProducesNotEmpty()) { if (response.Schema != null) { IModelType serviceType = response.Schema.GetBuilder(_swaggerModeler) .BuildServiceType(response.Schema.Reference.StripDefinitionPath()); Debug.Assert(serviceType != null); BuildMethodReturnTypeStack(serviceType, types); var compositeType = serviceType as CompositeType; if (compositeType != null) { VerifyFirstPropertyIsByteArray(compositeType); } method.Responses[responseStatusCode] = new Response(serviceType, headerType); handled = true; } } return handled; }
/// <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 context, SwaggerBase previous) { if (previous == null) { throw new ArgumentNullException("previous"); } context.CurrentRoot = this; context.PreviousRoot = previous; base.Compare(context, previous); var previousDefinition = previous as ServiceDefinition; if (previousDefinition == null) { throw new ArgumentException("Comparing a service definition with something else."); } if (Info != null && previousDefinition.Info != 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 = Regex.Replace(path, @"\{\w*\}", @"{}"); context.PushProperty(path); Dictionary <string, Operation> operations = null; if (!newPaths.TryGetValue(p, out operations)) { context.LogBreakingChange(ComparisonMessages.RemovedPath, path); } else { Dictionary <string, Operation> previousOperations = previousDefinition.Paths[path]; foreach (var previousOperation in previousOperations) { Operation newOperation = null; if (!operations.TryGetValue(previousOperation.Key, out newOperation)) { context.LogBreakingChange(ComparisonMessages.RemovedOperation, previousOperation.Value.OperationId); } } foreach (var operation in operations) { Operation previousOperation = null; if (previousDefinition.Paths[path].TryGetValue(operation.Key, out previousOperation)) { context.PushProperty(operation.Key); operation.Value.Compare(context, previousOperation); context.Pop(); } } } context.Pop(); } context.Pop(); newPaths = RemovePathVariables(CustomPaths); context.PushProperty("x-ms-paths"); foreach (var path in previousDefinition.CustomPaths.Keys) { var p = Regex.Replace(path, @"\{\w*\}", @"{}"); context.PushProperty(path); Dictionary <string, Operation> operations = null; if (!newPaths.TryGetValue(p, out operations)) { context.LogBreakingChange(ComparisonMessages.RemovedPath, path); } else { 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); } } 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(); } 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 if (schema.IsReferenced && oldSchema.IsReferenced) { context.PushProperty(def); schema.Compare(context, previousDefinition.Definitions[def]); context.Pop(); } } context.Pop(); context.Pop(); return(context.Messages); }
/// <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)) { if (previousDefinition.Paths[path].Count != 0 && previousDefinition.Paths[path].All(operation => operation.Value.Deprecated)) { // Entry path removed contained only deprecated operations, implies path was deprecated context.LogInfo(ComparisonMessages.RemovedDeprecatedpath, path); } else { // Entry 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)) { if (previousOperation.Value.Deprecated) { // Deprecated operation was removed from the path context.LogInfo(ComparisonMessages.RemovedDeprecatedOperation, previousOperation.Value.OperationId); } // 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); }