Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor [Sonar] #3189

Merged
merged 36 commits into from
Apr 15, 2019
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
1cfd606
refactor(sonar)
calebcartwright Mar 2, 2019
1a7e0a7
refactor(sonarqube): creating separate services for SQ badges
calebcartwright Mar 3, 2019
2d1df76
refactor(sonar): more sonar refactorings
calebcartwright Mar 11, 2019
61e9fa2
refactor(sonar): fixed duplicate service names from c/p
calebcartwright Mar 11, 2019
216f79e
refactor(sonar): finished violations service impl
calebcartwright Mar 12, 2019
45f1a2c
refactor(sonar): finished unit tests for violations service
calebcartwright Mar 12, 2019
579a612
feat(sonar): violation badge updates
calebcartwright Mar 20, 2019
ce5ed1f
refactor(sonar): finished doc. api density service
calebcartwright Mar 29, 2019
54f721d
feat(sonar): added quality gate service
calebcartwright Mar 29, 2019
cdfc56d
chore: sonar doc tweaks
calebcartwright Mar 29, 2019
d578ddb
refactor(sonar): added redirector service
calebcartwright Mar 31, 2019
1a4eac4
Merge branch 'master' into sonarqube-refactor
calebcartwright Mar 31, 2019
cdb959d
refactor(sonar): added examples
calebcartwright Mar 31, 2019
6e94272
refactor(sonar): minor example updates
calebcartwright Mar 31, 2019
f434469
refactor(sonar): added final tests
calebcartwright Mar 31, 2019
4743ab0
chore(sonar): removed unneeded test spec file for base class
calebcartwright Mar 31, 2019
fdc88b8
Merge branch 'master' into sonarqube-refactor
calebcartwright Apr 5, 2019
e01c8f6
Merge branch 'master' into sonarqube-refactor
calebcartwright Apr 8, 2019
256c0d0
Merge branch 'master' into sonarqube-refactor
calebcartwright Apr 9, 2019
fe7cb37
refactor(sonar): updates based on PR feedback
calebcartwright Apr 13, 2019
b4cad8c
refactor(sonar): change query param to sonarVersion
calebcartwright Apr 13, 2019
64ec70e
Merge branch 'master' into sonarqube-refactor
calebcartwright Apr 13, 2019
3d4971f
refactor(sonar): fixing query param issue
calebcartwright Apr 13, 2019
9fa94fd
refactor(sonar): fix test color for generic metric
calebcartwright Apr 13, 2019
40920bb
chore: fix lint/prettier issue
calebcartwright Apr 13, 2019
4ef8c90
Merge branch 'master' into sonarqube-refactor
calebcartwright Apr 14, 2019
c5bfe3d
chore(sonar): update query param name in examples
calebcartwright Apr 14, 2019
da0415c
Merge branch 'sonarqube-refactor' of https://github.com/badges/shield…
calebcartwright Apr 14, 2019
bc61f0a
refactor(sonar): make schema metric key required
calebcartwright Apr 14, 2019
44ac1ea
reactor(sonar): fix tests
calebcartwright Apr 14, 2019
e4d4e46
Merge branch 'master' into sonarqube-refactor
calebcartwright Apr 14, 2019
a2f3c0b
refactor(sonar): added more example listings
calebcartwright Apr 14, 2019
5478bee
refactor(sonar): minor style updates
calebcartwright Apr 14, 2019
5059863
refactor(sonar): update examples
calebcartwright Apr 14, 2019
e006670
refactor(Sonar): minor example tweaks
calebcartwright Apr 14, 2019
3ddd046
Merge branch 'master' into sonarqube-refactor
calebcartwright Apr 15, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions services/sonar/sonar-base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
'use strict'

const Joi = require('joi')
const serverSecrets = require('../../lib/server-secrets')
const { BaseJsonService } = require('..')
const { isLegacyVersion } = require('./sonar-helpers')

const schema = Joi.object({
component: Joi.object({
measures: Joi.array()
.items(
Joi.object({
metric: Joi.string(),
calebcartwright marked this conversation as resolved.
Show resolved Hide resolved
value: Joi.alternatives(
Joi.number().min(0),
Joi.allow('OK', 'ERROR')
).required(),
}).required()
)
.required(),
}).required(),
}).required()

const legacyApiSchema = Joi.array()
.items(
Joi.object({
msr: Joi.array()
.items(
Joi.object({
key: Joi.string(),
calebcartwright marked this conversation as resolved.
Show resolved Hide resolved
val: Joi.alternatives(
Joi.number().min(0),
Joi.allow('OK', 'ERROR')
).required(),
}).required()
)
.required(),
}).required()
)
.required()

module.exports = class SonarBase extends BaseJsonService {
transform({ json, version }) {
const useLegacyApi = isLegacyVersion({ version })
const rawValue = useLegacyApi
? json[0].msr[0].val
: json.component.measures[0].value
const value = parseInt(rawValue)

// Most values are numeric, but not all of them.
return { metricValue: value || rawValue }
}

async fetch({ version, protocol, host, component, metricName }) {
let qs, url
const useLegacyApi = isLegacyVersion({ version })

if (useLegacyApi) {
url = `${protocol}://${host}/api/resources`
qs = {
resource: component,
depth: 0,
metrics: metricName,
includeTrends: true,
}
} else {
url = `${protocol}://${host}/api/measures/component`
qs = {
componentKey: component,
metricKeys: metricName,
}
}

const options = { qs }

if (serverSecrets.sonarqube_token) {
options.auth = {
user: serverSecrets.sonarqube_token,
}
}

return this._requestJson({
schema: useLegacyApi ? legacyApiSchema : schema,
url,
options,
errorMessages: {
404: 'component or metric not found, or legacy API not supported',
chris48s marked this conversation as resolved.
Show resolved Hide resolved
},
})
}
}
4 changes: 4 additions & 0 deletions services/sonar/sonar-base.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
'use strict'

calebcartwright marked this conversation as resolved.
Show resolved Hide resolved
// const SonarQubeBase = require('./sonarqube-base')

42 changes: 42 additions & 0 deletions services/sonar/sonar-coverage.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use strict'

const { coveragePercentage } = require('../color-formatters')
const SonarBase = require('./sonar-base')
const { patternBase, queryParamSchema } = require('./sonar-helpers')

module.exports = class SonarCoverage extends SonarBase {
static get category() {
return 'coverage'
}

static get defaultBadgeData() {
return { label: 'coverage' }
}

static render({ coverage }) {
return {
message: `${coverage.toFixed(0)}%`,
color: coveragePercentage(coverage),
}
}

static get route() {
return {
base: 'sonar',
pattern: `${patternBase}/coverage`,
queryParamSchema,
}
}

async handle({ protocol, host, component }, { version }) {
const json = await this.fetch({
version,
protocol,
host,
component,
metricName: 'coverage',
})
const { metricValue: coverage } = this.transform({ json, version })
return this.constructor.render({ coverage })
}
}
22 changes: 22 additions & 0 deletions services/sonar/sonar-coverage.tester.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict'

const t = (module.exports = require('../tester').createServiceTester())
const { isIntegerPercentage } = require('../test-validators')

t.create('Coverage')
.get(
'/http/sonar.petalslink.com/org.ow2.petals%3Apetals-se-ase/coverage.json'
)
.expectBadge({
label: 'coverage',
message: isIntegerPercentage,
})

t.create('Coverage (legacy API supported)')
.get(
'/http/sonar.petalslink.com/org.ow2.petals%3Apetals-se-ase/coverage.json?version=4.2'
chris48s marked this conversation as resolved.
Show resolved Hide resolved
)
.expectBadge({
label: 'coverage',
message: isIntegerPercentage,
})
chris48s marked this conversation as resolved.
Show resolved Hide resolved
48 changes: 48 additions & 0 deletions services/sonar/sonar-documented-api-density.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use strict'

const SonarBase = require('./sonar-base')
const {
patternBase,
queryParamSchema,
getLabel,
goodMetricColorScale,
} = require('./sonar-helpers')

const metric = 'public_documented_api_density'

module.exports = class SonarDocumentedApiDensity extends SonarBase {
calebcartwright marked this conversation as resolved.
Show resolved Hide resolved
static get category() {
return 'analysis'
}

static get defaultBadgeData() {
return { label: getLabel({ metric }) }
}

static render({ density }) {
return {
message: `${density}%`,
color: goodMetricColorScale(density),
}
}

static get route() {
return {
base: 'sonar',
pattern: `${patternBase}/${metric}`,
queryParamSchema,
}
}

async handle({ protocol, host, component }, { version }) {
const json = await this.fetch({
version,
protocol,
host,
component,
metricName: metric,
})
const { metricValue: density } = this.transform({ json, version })
return this.constructor.render({ density })
}
}
29 changes: 29 additions & 0 deletions services/sonar/sonar-documented-api-density.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict'

const { test, given } = require('sazerac')
const SonarDocumentedApiDensity = require('./sonar-documented-api-density.service')

describe('SonarDocumentedApiDensity', function() {
test(SonarDocumentedApiDensity.render, () => {
given({ density: 0 }).expect({
message: '0%',
color: 'red',
})
given({ density: 10 }).expect({
message: '10%',
color: 'orange',
})
given({ density: 20 }).expect({
message: '20%',
color: 'yellow',
})
given({ density: 50 }).expect({
message: '50%',
color: 'yellowgreen',
})
given({ density: 100 }).expect({
message: '100%',
color: 'brightgreen',
})
})
})
22 changes: 22 additions & 0 deletions services/sonar/sonar-documented-api-density.tester.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict'

const { isIntegerPercentage } = require('../test-validators')
const t = (module.exports = require('../tester').createServiceTester())

t.create('Documented API Density')
.get(
'/http/sonar.petalslink.com/org.ow2.petals%3Apetals-se-ase/public_documented_api_density.json'
)
.expectBadge({
label: 'public documented api density',
message: isIntegerPercentage,
})

t.create('Documented API Density (legacy API supported)')
.get(
'/http/sonar.petalslink.com/org.ow2.petals%3Apetals-se-ase/public_documented_api_density.json?version=4.2'
)
.expectBadge({
label: 'public documented api density',
message: isIntegerPercentage,
})
51 changes: 51 additions & 0 deletions services/sonar/sonar-fortify-rating.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
'use strict'

const SonarBase = require('./sonar-base')
const { patternBase, queryParamSchema } = require('./sonar-helpers')

const colorMap = {
0: 'red',
1: 'orange',
2: 'yellow',
3: 'yellowgreen',
4: 'green',
5: 'brightgreen',
}

module.exports = class SonarFortifyRating extends SonarBase {
calebcartwright marked this conversation as resolved.
Show resolved Hide resolved
static get category() {
return 'analysis'
}

static get defaultBadgeData() {
return { label: 'fortify-security-rating' }
}

static render({ rating }) {
return {
message: `${rating}/5`,
color: colorMap[rating],
}
}

static get route() {
return {
base: 'sonar',
pattern: `${patternBase}/fortify-security-rating`,
queryParamSchema,
}
}

async handle({ protocol, host, component }, { version }) {
const json = await this.fetch({
version,
protocol,
host,
component,
metricName: 'fortify-security-rating',
})

const { metricValue: rating } = this.transform({ json, version })
return this.constructor.render({ rating })
}
}
72 changes: 72 additions & 0 deletions services/sonar/sonar-fortify-rating.tester.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
'use strict'

const sinon = require('sinon')
const t = (module.exports = require('../tester').createServiceTester())
const serverSecrets = require('../../lib/server-secrets')
const sonarToken = 'abc123def456'

// The below tests are using a mocked API response because
// neither SonarCloud.io nor any known public SonarQube deployments
// have the Fortify plugin installed and in use, so there are no
// available live endpoints to hit.
t.create('Fortify Security Rating')
.before(() => {
serverSecrets['sonarqube_token'] = undefined
sinon.stub(serverSecrets, 'sonarqube_token').value(sonarToken)
})
.get(
'/http/sonar.petalslink.com/org.ow2.petals%3Apetals-se-ase/fortify-security-rating.json'
)
.intercept(nock =>
nock('http://sonar.petalslink.com/api/measures')
.get('/component')
.query({
componentKey: 'org.ow2.petals:petals-se-ase',
metricKeys: 'fortify-security-rating',
})
.basicAuth({ user: sonarToken })
.reply(200, {
component: {
measures: [
{
metric: 'fortify-security-rating',
value: 4,
},
],
},
})
)
.finally(sinon.restore)
.expectBadge({
label: 'fortify-security-rating',
message: '4/5',
})

t.create('Fortify Security Rating (legacy API supported)')
.get(
'/http/sonar.petalslink.com/org.ow2.petals%3Apetals-se-ase/fortify-security-rating.json?version=4.2'
)
.intercept(nock =>
nock('http://sonar.petalslink.com/api')
.get('/resources')
.query({
resource: 'org.ow2.petals:petals-se-ase',
depth: 0,
metrics: 'fortify-security-rating',
includeTrends: true,
})
.reply(200, [
{
msr: [
{
metric: 'fortify-security-rating',
val: 3,
},
],
},
])
)
.expectBadge({
label: 'fortify-security-rating',
message: '3/5',
})
Loading