Skip to content

Commit fec1da2

Browse files
authored
Increase test coverage and refactor code to make it more testable (#11)
*increase test coverage * improved repo matching, enabled multiple repos per project
1 parent e45c1dd commit fec1da2

10 files changed

+441
-146
lines changed

Diff for: README.md

+8-5
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ To work, the Sentry Sourcegraph extension must know how to recognize instances o
2323
Set the following configurations in your settings:
2424

2525
```
26-
"sentry.organization": "[Organization Name]",
26+
"sentry.organization": "[Sentry organization name]",
2727
"sentry.projects": [
2828
{
29-
"name": "[Project Name]",
30-
"projectId": "[Project ID, e.g. "1334031"]",
29+
"name": "[Project name for the config overview, e.g. Webapp errors]",
30+
"projectId": "[Sentry project ID, e.g. "1334031"]",
3131
"patternProperties": {
32-
"repoMatch": "[repo name asociated with this project]",
32+
"repoMatch": "[RegExp[] repo names asociated with this Sentry project]",
3333
"fileMatches": [
3434
[RegExp[] that matches file format, e.g. "\\.tsx?"]
3535
],
@@ -60,7 +60,10 @@ File matches can also be narrowed down to certain folders by specifying this in
6060
"patternProperties": {
6161
"repoMatch": "sourcegraph",
6262
"fileMatches": ["([^'\"]+)\/.*\\.ts?"],
63-
"lineMatches": ["throw new Error+\\(['\"]([^'\"]+)['\"]\\)"]
63+
"lineMatches": [
64+
"throw new Error+\\(['\"]([^'\"]+)['\"]\\)",
65+
"console\\.(warn|debug|info|error)\\(['\"`]([^'\"`]+)['\"`]\\)"
66+
]
6467
}
6568
6669
```

Diff for: codecov.yml

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
11
coverage:
22
status:
3-
patch: off
3+
patch:
4+
default:
5+
threshold: 50%
6+
project:
7+
default:
8+
target: 80%
9+
ignore:
10+
- "src/test"

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@
140140
"mock-require": "^3.0.3",
141141
"nyc": "^13.3.0",
142142
"parcel-bundler": "^1.12.3",
143-
"prettier": "1.16.4",
143+
"prettier": "1.17.0",
144144
"rxjs": "^6.4.0",
145145
"sinon": "^7.3.1",
146146
"source-map-support": "^0.5.12",

Diff for: src/extension.ts

+48-27
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
isFileMatched,
99
matchSentryProject,
1010
} from './handler'
11-
import { resolveSettings, Settings } from './settings'
11+
import { resolveSettings, SentryProject, Settings } from './settings'
1212

1313
/**
1414
* Params derived from the document's URI.
@@ -27,9 +27,19 @@ const SENTRYORGANIZATION = SETTINGSCONFIG['sentry.organization']
2727
* are set in the sentry extension settings.
2828
*/
2929
const COMMON_ERRORLOG_PATTERNS = [
30+
// typescript/javascript
3031
/throw new Error+\(['"]([^'"]+)['"]\)/gi,
3132
/console\.(error|info|warn)\(['"`]([^'"`]+)['"`]\)/gi,
33+
// go
3234
/log\.(Printf|Print|Println)\(['"]([^'"]+)['"]\)/gi,
35+
/fmt\.Errorf\(['"]([^'"]+)['"]\)/gi,
36+
/errors\.New\(['"]([^'"]+)['"]\)/gi,
37+
/err\.message\(['"`]([^'"`]+)['"`]\)/gi,
38+
/panic\(['"]([^'"]+)['"]\)/gi,
39+
// python
40+
/raise (TypeError|ValueError)\(['"`]([^'"`]+)['"`]\)/gi,
41+
// java
42+
/logger\.(debug|error)\(['"`]([^'"`]+)['"`]\);/gi,
3343
]
3444

3545
export function activate(context: sourcegraph.ExtensionContext): void {
@@ -43,23 +53,30 @@ export function activate(context: sourcegraph.ExtensionContext): void {
4353
context.subscriptions.add(
4454
activeEditor.subscribe(editor => {
4555
const sentryProjects = SETTINGSCONFIG['sentry.projects']
46-
const decorations = getDecorations(editor, sentryProjects)
47-
editor.setDecorations(DECORATION_TYPE, decorations)
56+
if (editor.document.text) {
57+
const decorations = getDecorations(editor.document.uri, editor.document.text, sentryProjects)
58+
if (decorations.length === 0) {
59+
return
60+
}
61+
editor.setDecorations(DECORATION_TYPE, decorations)
62+
}
4863
})
4964
)
5065
}
5166
}
5267

5368
/**
5469
* Get and varify the necessary uri and config data and build the decorations.
55-
* @param editor
56-
* @param sentryProjects
70+
* @param documentUri the current document's URI
71+
* @param documentText content of the document being scanned for error handling code
72+
* @param sentryProjects list of Sentry projects sourced from the user's Sentry extension configurations
5773
*/
5874
export function getDecorations(
59-
editor: sourcegraph.CodeEditor,
60-
sentryProjects: Settings['sentry.projects']
75+
documentUri: string,
76+
documentText: string,
77+
sentryProjects?: SentryProject[]
6178
): sourcegraph.TextDocumentDecoration[] {
62-
const params: Params = getParamsFromUriPath(editor.document.uri)
79+
const params: Params = getParamsFromUriPath(documentUri)
6380
const sentryProject = sentryProjects && matchSentryProject(params, sentryProjects)
6481
let missingConfigData: string[] = []
6582
let fileMatched: boolean | null
@@ -75,39 +92,43 @@ export function getDecorations(
7592
}
7693

7794
return decorateEditor(
78-
editor,
7995
missingConfigData,
96+
documentText,
8097
sentryProject.projectId,
8198
sentryProject.patternProperties.lineMatches
8299
)
83100
}
84-
return decorateEditor(editor, missingConfigData)
101+
return decorateEditor(missingConfigData, documentText)
85102
}
86103

87104
/**
88105
* Build decorations by matching error handling code with either user config or common error patterns.
89-
* @param editor
90-
* @param missingConfigData
91-
* @param sentryProjectId
92-
* @param lineMatches
106+
* @param missingConfigData list of missing configs that will appear as a hover warning on the Sentry link
107+
* @param documentText content of the document being scanned for error handling code
108+
* @param sentryProjectId Sentry project id retrieved from Sentry extension settings
109+
* @param lineMatches line patching patterns set in the user's Sentry extension configurations
110+
* @return a list of decorations to render as links on each matching line
93111
*/
94112
// TODO: add tests for that new function (kind of like getBlameDecorations())
95-
function decorateEditor(
96-
editor: sourcegraph.CodeEditor,
113+
export function decorateEditor(
97114
missingConfigData: string[],
115+
documentText: string,
98116
sentryProjectId?: string,
99117
lineMatches?: RegExp[]
100118
): sourcegraph.TextDocumentDecoration[] {
101119
const decorations: sourcegraph.TextDocumentDecoration[] = []
102-
103-
for (const [index, line] of editor.document.text!.split('\n').entries()) {
120+
for (const [index, line] of documentText.split('\n').entries()) {
104121
let match: RegExpExecArray | null
105-
for (let pattern of lineMatches ? lineMatches : COMMON_ERRORLOG_PATTERNS) {
122+
for (let pattern of lineMatches && lineMatches.length > 0 ? lineMatches : COMMON_ERRORLOG_PATTERNS) {
106123
pattern = new RegExp(pattern, 'gi')
107124
do {
108125
match = pattern.exec(line)
109-
if (match) {
110-
decorations.push(decorateLine(index, match, missingConfigData, sentryProjectId))
126+
// Depending on the line matching pattern the query m is indexed in position 1 or 2.
127+
// TODO: Specify which capture group should be used through configuration.
128+
if (match && match.length <= 2) {
129+
decorations.push(decorateLine(index, match[1], missingConfigData, sentryProjectId))
130+
} else if (match && match.length > 2) {
131+
decorations.push(decorateLine(index, match[2], missingConfigData, sentryProjectId))
111132
}
112133
} while (match)
113134
pattern.lastIndex = 0 // reset
@@ -121,11 +142,13 @@ function decorateEditor(
121142
* or that matches common error loggin patterns.
122143
* @param index for decoration range
123144
* @param match for a line containing an error query
145+
* @param missingConfigData list of missing configs that will appear as a hover warning on the Sentry link
124146
* @param sentryProjectId Sentry project id retrieved from Sentry extension settings
147+
* @return either a successful or a warning decoration to render the Sentry link
125148
*/
126149
export function decorateLine(
127150
index: number,
128-
match: RegExpExecArray,
151+
match: string,
129152
missingConfigData: string[],
130153
sentryProjectId?: string
131154
): sourcegraph.TextDocumentDecoration {
@@ -134,19 +157,17 @@ export function decorateLine(
134157
range: new sourcegraph.Range(index, 0, index, 0),
135158
isWholeLine: true,
136159
after: {
137-
backgroundColor: missingConfigData.length === 0 ? '#e03e2f' : '#f2736d',
160+
backgroundColor: missingConfigData.length === 0 && sentryProjectId ? '#e03e2f' : '#f2736d',
138161
color: 'rgba(255, 255, 255, 0.8)',
139162
contentText: lineDecorationText.content,
140163
hoverMessage: lineDecorationText.hover,
141-
// Depending on the line matching pattern the query m is indexed in position 1 or 2.
142-
// TODO: Specify which capture group should be used through configuration.
143164
// TODO: If !SENTRYORGANIZATION is missing in config, link to $USER/settings and hint
144165
// user to fill it out.
145166
linkURL: !SENTRYORGANIZATION
146167
? ''
147168
: sentryProjectId
148-
? buildUrl(match.length > 2 ? match[2] : match[1], sentryProjectId).toString()
149-
: buildUrl(match.length > 2 ? match[2] : match[1]).toString(),
169+
? buildUrl(match, sentryProjectId).toString()
170+
: buildUrl(match).toString(),
150171
},
151172
}
152173
return decoration

Diff for: src/handler.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,13 @@ export function matchSentryProject(params: Params, projects: SentryProject[]): S
4949
// TODO: Handle the null case instead of using a non-null assertion !
5050
// TODO: Handle cases where the wrong project is matched due to similar repo name,
5151
// e.g. `sourcegraph-jetbrains` repo will match the `sourcegraph` project
52-
const project = projects.find(p => !!new RegExp(p.patternProperties.repoMatch).exec(params.repo!))
52+
53+
const project = projects.find(p =>
54+
p.patternProperties.repoMatch
55+
? !!p.patternProperties.repoMatch.find(repo => !!new RegExp(repo).exec(params.repo!))
56+
: false
57+
)
58+
5359
if (!project) {
5460
return undefined
5561
}
@@ -74,11 +80,10 @@ export function checkMissingConfig(settings: SentryProject): string[] {
7480
return []
7581
}
7682
const missingConfig: string[] = []
77-
7883
for (const [key, value] of Object.entries(settings)) {
7984
if (value instanceof Object) {
8085
for (const [k, v] of Object.entries(value)) {
81-
if (!v || Object.keys(v).length === 0) {
86+
if (!v || (v instanceof Object && Object.keys(v).length === 0)) {
8287
missingConfig.push(k)
8388
}
8489
}
@@ -107,7 +112,8 @@ export function createDecoration(
107112
} else if (missingConfigData.length > 0) {
108113
contentText = ' View logs in Sentry (❕)» '
109114
hoverText =
110-
' Please fill out the following configurations in your Sentry extension settings: ' + missingConfigData
115+
' Please fill out the following configurations in your Sentry extension settings: ' +
116+
missingConfigData.join(', ')
111117
}
112118
return {
113119
content: contentText,

Diff for: src/settings.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export interface SentryProject {
1313
name: string
1414
projectId: string
1515
patternProperties: {
16-
repoMatch: RegExp
16+
repoMatch?: RegExp[]
1717
fileMatches: RegExp[]
1818
// RexExp patterns to match log handeling code, e.g. /log\.(Printf|Print)\(['"]([^'"]+)['"]\)/
1919
lineMatches: RegExp[]

0 commit comments

Comments
 (0)