public override IEnumerable <ValidationMessage> GetValidationMessages(Dictionary <string, Dictionary <string, Operation> > entity, RuleContext context) { // get all operation objects that are either of get or post type ServiceDefinition serviceDefinition = context.Root; var listOperations = entity.Values.SelectMany(opDict => opDict.Where(pair => pair.Key.ToLower().Equals("get") || pair.Key.ToLower().Equals("post"))); foreach (var opPair in listOperations) { // if the operation id is not of type _list* or does not return an array type, skip if (!ListRegex.IsMatch(opPair.Value.OperationId) || !ValidationUtilities.IsXmsPageableResponseOperation(opPair.Value)) { continue; } string collType = opPair.Value.Responses.GetValueOrNull("200")?.Schema?.Reference?.StripDefinitionPath(); // if no response type defined skip if (collType == null) { continue; } var collTypeDef = serviceDefinition.Definitions[collType]; // if collection object has 2 properties or less (x-ms-pageable objects can have the nextlink prop) // and the object does not have a property named "value", show the warning if ((collTypeDef.Properties?.Count <= 2) && collTypeDef.Properties.All(prop => !(prop.Key.ToLower().Equals("value") && prop.Value.Type == DataType.Array))) { var violatingPath = ValidationUtilities.GetOperationIdPath(opPair.Value.OperationId, entity); yield return(new ValidationMessage(new FileObjectPath(context.File, context.Path.AppendProperty(violatingPath.Key).AppendProperty(opPair.Key).AppendProperty("responses").AppendProperty("200").AppendProperty("schema")), this, collType, opPair.Value.OperationId)); } } }
public override IEnumerable <ValidationMessage> GetValidationMessages(Dictionary <string, Dictionary <string, Operation> > entity, RuleContext context) { // get all operation objects that are either of get or post type var serviceDefinition = (ServiceDefinition)context.Root; var listOperations = entity.Values.SelectMany(opDict => opDict.Where(pair => pair.Key.ToLower().Equals("get") || pair.Key.ToLower().Equals("post"))); foreach (var opPair in listOperations) { // if the operation id is already of the type _list we can skip this check if (ListRegex.IsMatch(opPair.Value.OperationId)) { continue; } var violatingPath = ValidationUtilities.GetOperationIdPath(opPair.Value.OperationId, entity).Key; if (ValidationUtilities.IsXmsPageableResponseOperation(opPair.Value)) { yield return(new ValidationMessage(new FileObjectPath(context.File, context.Path.AppendProperty(violatingPath).AppendProperty(opPair.Key).AppendProperty("operationId")), this, opPair.Value.OperationId, XmsPageableViolation)); } else if (ValidationUtilities.IsArrayTypeResponseOperation(opPair.Value, serviceDefinition)) { yield return(new ValidationMessage(new FileObjectPath(context.File, context.Path.AppendProperty(violatingPath).AppendProperty(opPair.Key).AppendProperty("operationId")), this, opPair.Value.OperationId, ArrayTypeViolation)); } } }
// Verifies if a tracked resource has a corresponding PATCH operation public override IEnumerable <ValidationMessage> GetValidationMessages(Dictionary <string, Dictionary <string, Operation> > paths, RuleContext context) { var serviceDefinition = context.Root; var ops = ValidationUtilities.GetOperationsByRequestMethod("put", serviceDefinition); var putRespModels = ops.Select(op => (op.Responses?.ContainsKey("200") != true) ? null : op.Responses["200"].Schema?.Reference?.StripDefinitionPath()); putRespModels = putRespModels.Where(modelName => !string.IsNullOrEmpty(modelName)); var xmsResModels = ValidationUtilities.GetXmsAzureResourceModels(serviceDefinition.Definitions); var violatingModels = putRespModels.Where(respModel => !ValidationUtilities.EnumerateModelHierarchy(respModel, serviceDefinition.Definitions).Intersect(xmsResModels).Any()); foreach (var model in violatingModels) { var violatingOp = ops.Where(op => (op.Responses?.ContainsKey("200") == true) && op.Responses["200"]?.Schema?.Reference?.StripDefinitionPath() == model).First(); var violatingPath = ValidationUtilities.GetOperationIdPath(violatingOp.OperationId, paths); var violatingOpVerb = ValidationUtilities.GetOperationIdVerb(violatingOp.OperationId, violatingPath); yield return(new ValidationMessage(new FileObjectPath(context.File, context.Path.AppendProperty(violatingPath.Key).AppendProperty(violatingOpVerb)), this, violatingOp.OperationId, model)); } }
/// Verifies if a PUT operation request and response schemas match public override IEnumerable <ValidationMessage> GetValidationMessages(Dictionary <string, Dictionary <string, Operation> > paths, RuleContext context) { var serviceDefinition = (ServiceDefinition)context.Root; var ops = ValidationUtilities.GetOperationsByRequestMethod("put", serviceDefinition); foreach (var op in ops) { // if PUT operation does not have any request parameters, skip, let some other validation rule handle it // if no 200 response exists, skip, let some other validation rule handle empty PUT response operations if (op.Parameters?.Any() != true || op.Responses?.ContainsKey("200") != true || serviceDefinition.Definitions?.Any() != true) { continue; } // look for the request body schema in the operation parameters section as well as the global parameters section string reqBodySchema = null; if (op.Parameters.Any(p => p.In == ParameterLocation.Body)) { reqBodySchema = op.Parameters.First(p => p.In == ParameterLocation.Body).Schema?.Reference?.StripDefinitionPath(); } else { var opGlobalParams = op.Parameters.Where(p => serviceDefinition.Parameters.ContainsKey(p.Reference?.StripParameterPath() ?? "")); if (opGlobalParams.Any()) { reqBodySchema = opGlobalParams.FirstOrDefault(p => p.In == ParameterLocation.Body)?.Schema?.Reference?.StripDefinitionPath(); } } // if no body parameters were found, skip, let some other validation handle an empty body put operation if (string.IsNullOrEmpty(reqBodySchema) || !serviceDefinition.Definitions.ContainsKey(reqBodySchema)) { continue; } var respModel = op.Responses["200"]?.Schema?.Reference?.StripDefinitionPath() ?? string.Empty; // if the 200 response schema does not match the request body parameter schema, flag violation if (respModel != reqBodySchema) { var violatingPath = ValidationUtilities.GetOperationIdPath(op.OperationId, paths); var violatingOpVerb = ValidationUtilities.GetOperationIdVerb(op.OperationId, violatingPath); yield return(new ValidationMessage(new FileObjectPath(context.File, context.Path.AppendProperty(violatingPath.Key).AppendProperty(violatingOpVerb)), this, op.OperationId, reqBodySchema, respModel)); } } }