-
-
Notifications
You must be signed in to change notification settings - Fork 240
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement text and teamcity formatters (#825)
* Add two formatters: text, teamcity. Closes issues #823 and #822. The text formatter is close to the stylish but with no special formatting and a source:line:column suitable for navigation within an IDE console (IntelliJ specifically). The teamcity formatter emits teamcity inspection service messages that teamcity will automatically detect and add to the build inspection tab. * Updates as per P0lip
- Loading branch information
Showing
8 changed files
with
124 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
10.15.3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { teamcity } from '../teamcity'; | ||
|
||
const mixedErrors = require('./__fixtures__/mixed-errors.json'); | ||
|
||
describe('Teamcity formatter', () => { | ||
test('should format messages', () => { | ||
const result = teamcity(mixedErrors); | ||
expect(result) | ||
.toContain(`##teamcity[inspectionType category='openapi' id='info-contact' name='info-contact' description='hint -- Info object should contain \`contact\` object.'] | ||
##teamcity[inspection typeId='info-contact' file='/home/Stoplight/spectral/src/__tests__/__fixtures__/petstore.oas3.json' line='3' message='hint -- Info object should contain \`contact\` object.'] | ||
##teamcity[inspectionType category='openapi' id='info-description' name='info-description' description='warning -- OpenAPI object info \`description\` must be present and non-empty string.'] | ||
##teamcity[inspection typeId='info-description' file='/home/Stoplight/spectral/src/__tests__/__fixtures__/petstore.oas3.json' line='3' message='warning -- OpenAPI object info \`description\` must be present and non-empty string.'] | ||
##teamcity[inspectionType category='openapi' id='info-matches-stoplight' name='info-matches-stoplight' description='error -- Info must contain Stoplight'] | ||
##teamcity[inspection typeId='info-matches-stoplight' file='/home/Stoplight/spectral/src/__tests__/__fixtures__/petstore.oas3.json' line='5' message='error -- Info must contain Stoplight'] | ||
##teamcity[inspectionType category='openapi' id='operation-description' name='operation-description' description='information -- Operation \`description\` must be present and non-empty string.'] | ||
##teamcity[inspection typeId='operation-description' file='/home/Stoplight/spectral/src/__tests__/__fixtures__/petstore.oas3.json' line='17' message='information -- Operation \`description\` must be present and non-empty string.'] | ||
##teamcity[inspectionType category='openapi' id='operation-description' name='operation-description' description='information -- Operation \`description\` must be present and non-empty string.'] | ||
##teamcity[inspection typeId='operation-description' file='/home/Stoplight/spectral/src/__tests__/__fixtures__/petstore.oas3.json' line='64' message='information -- Operation \`description\` must be present and non-empty string.'] | ||
##teamcity[inspectionType category='openapi' id='operation-description' name='operation-description' description='information -- Operation \`description\` must be present and non-empty string.'] | ||
##teamcity[inspection typeId='operation-description' file='/home/Stoplight/spectral/src/__tests__/__fixtures__/petstore.oas3.json' line='86' message='information -- Operation \`description\` must be present and non-empty string.']`); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { text } from '../text'; | ||
|
||
const mixedErrors = require('./__fixtures__/mixed-errors.json'); | ||
|
||
describe('Text formatter', () => { | ||
test('should format messages', () => { | ||
const result = text(mixedErrors); | ||
expect(result) | ||
.toContain(`/home/Stoplight/spectral/src/__tests__/__fixtures__/petstore.oas3.json:3:10 hint info-contact "Info object should contain \`contact\` object." | ||
/home/Stoplight/spectral/src/__tests__/__fixtures__/petstore.oas3.json:3:10 warning info-description "OpenAPI object info \`description\` must be present and non-empty string." | ||
/home/Stoplight/spectral/src/__tests__/__fixtures__/petstore.oas3.json:5:14 error info-matches-stoplight "Info must contain Stoplight" | ||
/home/Stoplight/spectral/src/__tests__/__fixtures__/petstore.oas3.json:17:13 information operation-description "Operation \`description\` must be present and non-empty string." | ||
/home/Stoplight/spectral/src/__tests__/__fixtures__/petstore.oas3.json:64:14 information operation-description "Operation \`description\` must be present and non-empty string." | ||
/home/Stoplight/spectral/src/__tests__/__fixtures__/petstore.oas3.json:86:13 information operation-description "Operation \`description\` must be present and non-empty string."`); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { Dictionary, Optional } from '@stoplight/types'; | ||
import { IRuleResult } from '../types'; | ||
import { Formatter } from './types'; | ||
import { getSeverityName, groupBySource, sortResults } from './utils'; | ||
|
||
function escapeString(str: Optional<string | number>) { | ||
if (str === void 0) { | ||
return ''; | ||
} | ||
return String(str) | ||
.replace(/\|/g, '||') | ||
.replace(/'/g, "|'") | ||
.replace(/\n/g, '|n') | ||
.replace(/\r/g, '|r') | ||
.replace(/\u0085/g, '|x') // TeamCity 6 | ||
.replace(/\u2028/g, '|l') // TeamCity 6 | ||
.replace(/\u2029/g, '|p') // TeamCity 6 | ||
.replace(/\[/g, '|[') | ||
.replace(/\]/g, '|]'); | ||
} | ||
|
||
function inspectionType(result: IRuleResult) { | ||
const code = escapeString(result.code); | ||
const severity = getSeverityName(result.severity); | ||
const message = escapeString(result.message); | ||
return `##teamcity[inspectionType category='openapi' id='${code}' name='${code}' description='${severity} -- ${message}']`; | ||
} | ||
|
||
function inspection(result: IRuleResult) { | ||
const code = escapeString(result.code); | ||
const severity = getSeverityName(result.severity); | ||
const message = escapeString(result.message); | ||
const line = result.range.start.line + 1; | ||
return `##teamcity[inspection typeId='${code}' file='${result.source}' line='${line}' message='${severity} -- ${message}']`; | ||
} | ||
|
||
function renderResults(results: IRuleResult[], parentIndex: number) { | ||
return sortResults(results) | ||
.map(result => `${inspectionType(result)}\n${inspection(result)}`) | ||
.join('\n'); | ||
} | ||
|
||
function renderGroupedResults(groupedResults: Dictionary<IRuleResult[]>) { | ||
return Object.keys(groupedResults) | ||
.map((source, index) => renderResults(groupedResults[source], index)) | ||
.join('\n'); | ||
} | ||
|
||
export const teamcity: Formatter = results => { | ||
const groupedResults = groupBySource(results); | ||
return renderGroupedResults(groupedResults); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { Dictionary } from '@stoplight/types'; | ||
import { IRuleResult } from '../types'; | ||
import { Formatter } from './types'; | ||
import { getSeverityName, groupBySource, sortResults } from './utils'; | ||
|
||
function renderResults(results: IRuleResult[], parentIndex: number) { | ||
return sortResults(results) | ||
.map(result => { | ||
const line = result.range.start.line + 1; | ||
const character = result.range.start.character + 1; | ||
const severity = getSeverityName(result.severity); | ||
return `${result.source}:${line}:${character} ${severity} ${result.code} "${result.message}"`; | ||
}) | ||
.join('\n'); | ||
} | ||
|
||
function renderGroupedResults(groupedResults: Dictionary<IRuleResult[]>) { | ||
return Object.keys(groupedResults) | ||
.map((source, index) => renderResults(groupedResults[source], index)) | ||
.join('\n'); | ||
} | ||
|
||
export const text: Formatter = results => { | ||
const groupedResults = groupBySource(results); | ||
return renderGroupedResults(groupedResults); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters