/// <summary> /// Validate if a resource has an id property on response. /// Supressions: Rule,Path,Operation,ResponseCode /// </summary> public IdPropertyResponseRule(OpenApiDocument contract, Supressions supressions, IOptions <RuleSettings> ruleSettings, OpenApiDocumentCache cache) : base(contract, supressions, ruleSettings, cache, ruleName, Severity.Warning) { idPropertyName = ruleSettings.Value.IdPropertyResponse.IdPropertyName; }
private protected override void ExecuteRuleLogic() { if (!string.IsNullOrEmpty(Contract.Info.Description) && !Contract.Info.Description.BeginsUpperAndFinishesWithPeriod()) { listResult.Add( new ResultItem(this, $"info.description:{Contract.Info.Description}")); } foreach (KeyValuePair <string, OpenApiPathItem> path in Contract.Paths.Where(path => !Supressions.IsSupressed(ruleName, path.Key))) { if ( !string.IsNullOrEmpty(path.Value.Description) && !path.Value.Description.BeginsUpperAndFinishesWithPeriod() || !string.IsNullOrEmpty(path.Value.Summary) && !path.Value.Summary.BeginsUpperAndFinishesWithPeriod() ) { listResult.Add( new ResultItem(this, path: path.Key)); } foreach (var parameter in Contract.GetAllParameters().Where(x => x.Path == path.Key)) { if (Supressions.IsSupressed(ruleName, path.Key, parameter.Method)) { continue; } if (Supressions.IsSupressed(ruleName, path.Key, parameter.Method, parameter.Name)) { continue; } if (!string.IsNullOrEmpty(parameter.OpenApiParameter.Description) && !parameter.OpenApiParameter.Description.BeginsUpperAndFinishesWithPeriod()) { listResult.Add(new ResultItem(this, parameter.ResultLocation())); } } foreach (KeyValuePair <OperationType, OpenApiOperation> operation in path.Value.Operations) { foreach (KeyValuePair <string, OpenApiResponse> response in operation.Value.Responses) { if (Supressions.IsSupressed(ruleName, path.Key, Convert.ToString(operation.Key.ToString().ToLowerInvariant()), string.Empty, response.Key)) { continue; } //Usually, response description only have the response code and description without punctuation. //Ex: 200 Ok foreach (KeyValuePair <string, OpenApiMediaType> content in response.Value.Content) { if (content.Value.Schema != null) { foreach (KeyValuePair <string, OpenApiSchema> firstLevelproperty in content.Value.Schema .Properties) { //fist level: data, pagination, etc. //second level: resource properties first level. listResult.TryAddEmptiableRange( CheckInnerPropertyDescription(path.Key, operation.Key.ToString().ToLower(), response.Key, content.Key, firstLevelproperty.Key, firstLevelproperty.Value.Properties) ); } } } } } } }
/// <summary> /// Try to test if the base URL parts togheter compose a valid URL. /// Supressions: Rule /// </summary> public BaseUrlRule(OpenApiDocument contract, Supressions supressions, IOptions <RuleSettings> ruleSettings, OpenApiDocumentCache cache) : base(contract, supressions, ruleSettings, cache, ruleName, Severity.Error) { }
/// <summary> /// Validates if 200 or 206 HTTP Status codes are wrongly with empty content as answer. /// Supressions: Rule,Path,Operation,ResponseCode /// </summary> public Empty200Rule(OpenApiDocument contract, Supressions supressions, IOptions <RuleSettings> ruleSettings, OpenApiDocumentCache cache) : base(contract, supressions, ruleSettings, cache, ruleName, Severity.Warning) { this.cache = cache; }
/// <summary> /// Validates if descriptions or summaries have some quality level, as begining with upper letter and finishing with period. /// Supressions: Rule,Path,Operation,parameter,ResponseCode,PropertyFull /// </summary> public DescriptionQualityRule(OpenApiDocument contract, Supressions supressions, IOptions <RuleSettings> ruleSettings, OpenApiDocumentCache cache) : base(contract, supressions, ruleSettings, cache, ruleName, Severity.Information) { }
/// <summary> /// Validate if paths have names that represents CRUD actions. /// Supressions: Rule,Path /// </summary> public PathWithCrudNamesRule(OpenApiDocument contract, Supressions supressions, IOptions <RuleSettings> ruleSettings, OpenApiDocumentCache cache) : base(contract, supressions, ruleSettings, cache, ruleName, Severity.Warning) { wordsToAvoid = ruleSettings.Value.PathWithCrudNames.WordsToAvoid.Split(','); }
/// <summary> /// Validate if a resource that returns some 3XX answers has Location header. /// Supressions: Rule,Path,Operation,ResponseCode /// </summary> public Http3xxWithoutLocationHeaderRule(OpenApiDocument contract, Supressions supressions, IOptions <RuleSettings> ruleSettings, OpenApiDocumentCache cache) : base(contract, supressions, ruleSettings, cache, ruleName, Severity.Warning) { this.cache = cache; }
private protected override void ExecuteRuleLogic() { var parameters = Contract.GetAllParameters(); //Search for parameters in query, path or header with examples that value is likelihood date and the format attribute is not date or date-time. foreach (OpenApiDocumentExtensions.Parameter parameter in parameters) { if (Supressions.IsSupressed(ruleName, parameter.Path)) { continue; } if (Supressions.IsSupressed(ruleName, parameter.Path, parameter.Method)) { continue; } if (Supressions.IsSupressed(ruleName, parameter.Path, parameter.Method, parameter.Name)) { continue; } //Parameters in query, path or header if (parameter.OpenApiParameter.Example != null && parameter.OpenApiParameter.Examples?.Count > 0 && parameter.OpenApiParameter.Schema.Type.ToLower() == "string") { if (parameter.OpenApiParameter.Example is OpenApiString exApiString && DateTime.TryParse(exApiString.Value, out _) && parameter.OpenApiParameter.Schema.Format != "date" && parameter.OpenApiParameter.Schema.Format != "date-time") { listResult.Add(new ResultItem(this) { Value = parameter.ResultLocation() }); } foreach (KeyValuePair <string, OpenApiExample> example in parameter.OpenApiParameter.Examples) { if (parameter.OpenApiParameter.Schema?.Type?.ToLower() == "string" && example.Value.Value is OpenApiString apiString && DateTime.TryParse(apiString.Value, out _) && parameter.OpenApiParameter.Schema.Format != "date" && parameter.OpenApiParameter.Schema.Format != "date-time") { listResult.Add(new ResultItem(this) { Value = parameter.ResultLocation() }); } } } } //Search for properties in request and response bodies with examples that value is likelihood date and the format is not date or date-time. var propertiesWithLikelyNumericExample = Contract.GetAllBodyProperties(RuleSettings, Cache) .Where(property => property.OpenApiSchemaObject?.Type?.ToLower() == "string" && property.Example is OpenApiString apiString && DateTime.TryParse(apiString.Value, out _) && property.OpenApiSchemaObject.Format != "date" && property.OpenApiSchemaObject.Format != "date-time" ) .ToList(); foreach (var property in propertiesWithLikelyNumericExample) { if (Supressions.IsSupressed(ruleName, property.Path, property.Operation.ToLowerInvariant(), property.FullName, property.ResponseCode)) { continue; } listResult.Add(new ResultItem(this) { Value = property.ResultLocation() }); } }
/// <summary> /// Validates if parameters or attributes have examples indicating that the content is likely date type and the format is not date or date-time. /// Supressions: Rule,Path,Operation,ResponseCode,Content,PropertyFull /// </summary> public DateWithoutFormatRule(OpenApiDocument contract, Supressions supressions, IOptions <RuleSettings> ruleSettings, OpenApiDocumentCache cache) : base(contract, supressions, ruleSettings, cache, ruleName, Severity.Hint) { }
/// <summary> /// Validate the properties case type. /// Supressions: Rule,Path,Operation,ResponseCode,PropertyFull /// </summary> public NestingDepthRule(OpenApiDocument contract, Supressions supressions, IOptions <RuleSettings> ruleSettings, OpenApiDocumentCache cache) : base(contract, supressions, ruleSettings, cache, ruleName, Severity.Warning) { maxDepth = ruleSettings.Value.NestingDepth.Depth; }