Skip to content

Commit

Permalink
✨ Manage subdirectory for rules
Browse files Browse the repository at this point in the history
  • Loading branch information
ouvreboite committed Jun 14, 2024
1 parent 48dbfca commit 8f5055a
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 46 deletions.
26 changes: 14 additions & 12 deletions examples/valid/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Valid example

* This example contains rules accross several markdown files: [rules1.md](rules1.md) and [rules2.md](rules2.md)
* This example contains rules accross several markdown files: [rules1.md](rules1.md) and [rules2.md](rules2.md) and even subdirectories [subdirectory/subrules.md](subdirectory/subrules.md)
* ✅ All the test declared in those files are expected to pass
* Some of the rules use custom functions defined in the [functions](functions) directory
* [spectral.base.yaml](spectral.base.yaml) contains the base rule files that will be used to merge the rules into. For example, it contains the extends, aliases and functions declarations
Expand All @@ -9,21 +9,23 @@
## Test ouput

```
npx poltergust test .\examples\valid
npx poltergust .\examples\valid
🔎 Testing the spectral rules from the .md files in the directory: .\examples\valid
👻 base-path-must-start-with-slash (examples\valid\rules1.md:40)
✅ Test OK (examples\valid\rules1.md:17)
✅ Test OK (examples\valid\rules1.md:27)
👻 operation-parameters-must-have-description (examples\valid\rules1.md:91)
✅ Test OK (examples\valid\rules1.md:58)
✅ Test OK (examples\valid\rules1.md:73)
👻 operation-must-have-description (examples\valid\rules1.md:107)
👻 operation-must-have-no-summary (examples\valid\rules1.md:122)
👻 operation-must-have-at-least-one-response (examples\valid\rules1.md:135)
👻 request-bodies-must-have-a-content (examples\valid\rules1.md:152)
👻 base-path-must-start-with-slash (examples\valid\rules1.md:39)
✅ Test OK (examples\valid\rules1.md:16)
✅ Test OK (examples\valid\rules1.md:26)
👻 operation-parameters-must-have-description (examples\valid\rules1.md:90)
✅ Test OK (examples\valid\rules1.md:57)
✅ Test OK (examples\valid\rules1.md:72)
👻 operation-must-have-description (examples\valid\rules1.md:106)
👻 operation-must-have-no-summary (examples\valid\rules1.md:121)
👻 operation-must-have-at-least-one-response (examples\valid\rules1.md:134)
👻 path-parameters-must-be-kebab-case (examples\valid\rules2.md:49)
✅ Test OK (examples\valid\rules2.md:11)
👻 required-property-must-exist (examples\valid\rules2.md:96)
✅ Test OK (examples\valid\rules2.md:66)
👻 request-bodies-must-have-a-content (examples\valid\subdirectory\subrules.md:41)
✅ Test OK (examples\valid\subdirectory\subrules.md:8)
✅ Test OK (examples\valid\subdirectory\subrules.md:25)
✅ Spectral rules merged in the file: examples\valid\spectral.yaml
```
18 changes: 0 additions & 18 deletions examples/valid/rules1.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
3. [operation-must-have-description](#operation-must-have-description)
4. [operation-must-have-no-summary](#operation-must-have-no-summary)
5. [operation-must-have-at-least-one-response](#operation-must-have-at-least-one-response)
6. [request-bodies-must-have-a-content](#request-bodies-must-have-a-content)

## base-path-must-start-with-slash

Expand Down Expand Up @@ -144,21 +143,4 @@ operation-must-have-at-least-one-response:
function: length
functionOptions:
min: 1
```
## request-bodies-must-have-a-content
```yaml
#👻-rule
request-bodies-must-have-a-content:
description: Request bodies must have a content
given: $.paths[*][*].requestBody
severity: error
then:
- field: content
function: truthy
- field: content
function: length
functionOptions:
min: 1
```
24 changes: 12 additions & 12 deletions examples/valid/spectral.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,6 @@ rules:
functionOptions:
min: 1

request-bodies-must-have-a-content:
description: Request bodies must have a content
given: $.paths[*][*].requestBody
severity: error
then:
- field: content
function: truthy
- field: content
function: length
functionOptions:
min: 1

path-parameters-must-be-kebab-case:
description: Path parameters must be kebab case
given: "#parameters[?(@.in==\"path\")]"
Expand All @@ -84,4 +72,16 @@ rules:
severity: error
then:
function: isRequiredPropertyDefined

request-bodies-must-have-a-content:
description: Request bodies must have a content
given: $.paths[*][*].requestBody
severity: error
then:
- field: content
function: truthy
- field: content
function: length
functionOptions:
min: 1

55 changes: 55 additions & 0 deletions examples/valid/subdirectory/subrules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Parameters rules

1. [request-bodies-must-have-a-content](#request-bodies-must-have-a-content)

## request-bodies-must-have-a-content

```yaml
#👻-failures: 0 request-bodies-must-have-a-content
openapi: 3.0.1
paths:
/pets:
post:
requestBody:
required: true
content:
application/json:
schema:
type: string
responses:
'201':
description: Created
```
```yaml
#👻-failures: 1 request-bodies-must-have-a-content
openapi: 3.0.1
paths:
/pets:
post:
requestBody: #👻-fails-here: request-bodies-must-have-a-content
required: true
responses:
'201':
description: Created
```
<details>
<summary>Spectral rule 🤖</summary>
```yaml
#👻-rule
request-bodies-must-have-a-content:
description: Request bodies must have a content
given: $.paths[*][*].requestBody
severity: error
then:
- field: content
function: truthy
- field: content
function: length
functionOptions:
min: 1
```
</details>
9 changes: 5 additions & 4 deletions src/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import { bundleAndLoadRuleset } from "@stoplight/spectral-ruleset-bundler/with-l

/**
* @param {string} rulesDir
* @returns {Object.<string, {rule: {name: string, content: string, line: number, filePath: string}, testCases: {content: string, assertions: {ruleName: string, line: number, failuresCount: number}[], line: number, filePath: string}[]>} the spectral rules and test cases by rule name
* @returns {Object.<string, {rule: {name: string, content: string, line: number, filePath: string}, testCases: {content: string, assertions: {ruleName: string, line: number, failuresCount: number}[], line: number, filePath: string}[]}>} the spectral rules and test cases by rule name
*/
export function extractAllRulesAndTestCases(rulesDir) {
const markdownFiles = fs.readdirSync(rulesDir).filter(f => f.endsWith('.md'));
//find all markdown files in the rules directory and subdirectories
const markdownFiles = fs.readdirSync(rulesDir, {encoding: "utf8", recursive: true}).filter(f => f.endsWith('.md'));

/** @type {Object.<string, {rule: {name: string, content: string, line: number, filePath: string}, testCases: {content: string, assertions: {ruleName: string, line: number, failuresCount: number}[], line: number, filePath: string}[]>} */
let byRuleName = {};
Expand All @@ -34,14 +35,14 @@ export function extractAllRulesAndTestCases(rulesDir) {

/**
* @param {string} filePath
* @param {Object.<string, {rule: {name: string, content: string, line: number, filePath: string}, testCases: {content: string, assertions: {ruleName: string, line: number, failuresCount: number}[], line: number, filePath: string}[]>} byRuleName
* @param {Object.<string, {rule: {name: string, content: string, line: number, filePath: string}, testCases: {content: string, assertions: {ruleName: string, line: number, failuresCount: number}[], line: number, filePath: string}[]}>} byRuleName
*/
export function extractRulesAndTestCases(filePath, byRuleName) {
const blocks = extractYamlBlocks(filePath);

blocks.forEach(block => {
const rule = extractRuleFromBlock(block, filePath);
const testCase = extractTestCaseBlock(block, filePath);
const testCase = extractTestCaseBlock(block);

//invalid states
if(!rule && !testCase){
Expand Down

0 comments on commit 8f5055a

Please sign in to comment.