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

Fixes issue description and octokit pagination #28

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 18 additions & 9 deletions lib/github/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const chalk = require('chalk')
const getBatchIssue = require('./batch')
const { conf } = require('../config')
const { getProjectName, getGraph } = require('./utils')
const { dressError } = require('../utils')

const { getLabels, ensureLabelsAreCreated } = require('./labels')

Expand All @@ -22,19 +23,27 @@ module.exports = {

// retrieve issue IDs already created in GitHub
async existingIssues () {
return await octokit.paginate(
`GET /search/issues?q=repo%3A${conf.ghOwner}/${conf.ghRepo}+is%3Aissue+label%3Asnyk`,
(response) =>
response.data.map((existingIssue) => [
existingIssue.title,
existingIssue.number
])
)
try {
return await octokit.paginate(
`GET /search/issues?q=repo%3A${conf.ghOwner}/${conf.ghRepo}+is%3Aissue+label%3Asnyk`,
(response) =>
response.data.map((existingIssue) => [
existingIssue.title,
existingIssue.number
])
)
} catch (err) {
throw new Error(dressError(err, `Failed to paginate octokit request for existing issues in repository ${conf.ghRepo}`))
}
},

async createIssue (options) {
if (conf.dryRun) return
return await this.client.issues.create(options)
try {
return await this.client.issues.create(options)
} catch (err) {
throw new Error(dressError(err, `Failed to ${options.issue_number ? 'update' : 'create'} issue: ${options.issue_number ? options.issue_number : options.title}`))
}
},

async createIssues (issues, existingIssues) {
Expand Down
44 changes: 24 additions & 20 deletions lib/github/labels.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict'

const { conf } = require('../config')
const { uniq } = require('../utils')
const { uniq, dressError } = require('../utils')

const LABELS = {
snyk: {
Expand Down Expand Up @@ -50,32 +50,36 @@ const getLabelAttributes = (name) => {

const ensureLabelsAreCreated = async (octokit, client, ghOwner, ghRepo, issues) => {
const labels = getLabels(issues)
const responseData = await octokit.paginate(
await client.issues.listLabelsForRepo({
owner: ghOwner,
repo: ghRepo,
per_page: 100
}),
(response) => response.data
)

let responseData
try {
responseData = await octokit.paginate(`GET /repos/${conf.ghOwner}/${conf.ghRepo}/labels?per_page=100`)
} catch (err) {
throw new Error(dressError(err, `Failed to paginate octokit request for labels in repository: ${ghRepo}`))
}

const currentLabels = responseData.map((x) => x.name)
const labelsToCreate = labels.filter((x) => !currentLabels.includes(x))
if (!labelsToCreate.length || conf.dryRun) {
return
}

await Promise.all(
labelsToCreate.map((name) =>
client.issues
.createLabel({
owner: ghOwner,
repo: ghRepo,
...getLabelAttributes(name)
})
.then(() => {
console.log(`Created GitHub label: "${name}"`)
})
)
labelsToCreate.map((name) => {
try {
return client.issues
.createLabel({
owner: ghOwner,
repo: ghRepo,
...getLabelAttributes(name)
})
.then(() => {
console.log(`Created GitHub label: "${name}"`)
})
} catch (err) {
throw new Error(dressError(err, `Failed to create GitHub label '${name}' in repository: ${ghRepo}`))
}
})
)
}

Expand Down
79 changes: 53 additions & 26 deletions lib/snyk.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
'use strict'

const request = require('request-promise-native')
const { dressError } = require('./utils')

const baseV1Url = 'https://snyk.io/api/v1'
const baseRestUrl = 'https://api.snyk.io'
const baseRestUrl = 'https://api.snyk.io/rest'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to include the full base here and just remove the '/rest' in the one place where it is redundant.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeramysoucy I think this is now introducing this when used Error: Failed to paginate request for get https://api.snyk.io/rest/rest/orgs/, notice the double /rest

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related issue: #30


module.exports = class Snyk {
constructor ({ token, orgId, minimumSeverity }) {
Expand All @@ -29,25 +30,47 @@ module.exports = class Snyk {
).orgs
}

async queryProjectDetails (organizationId, projectId) {
try {
return await request({
method: 'get',
url: `${baseV1Url}/org/${organizationId}/project/${projectId}`,
headers: this._headers,
json: true
})
} catch (err) {
throw new Error(dressError(err, `Failed to query snyk project details. Organization ID: ${organizationId}, Project ID: ${projectId}`))
}
}

async projects (orgId, selectedProjects = []) {
const organizationId = orgId || this._orgId

const responseData = await paginateRestResponseData(
`${baseRestUrl}/rest/orgs/${organizationId}/projects?version=2023-11-27&meta.latest_issue_counts=true&limit=20`,
`${baseRestUrl}/orgs/${organizationId}/projects?version=2023-11-27&meta.latest_issue_counts=true&limit=20`,
this._headers
)

return responseData.map((project) => {
const { critical, high, medium, low } = project.meta.latest_issue_counts
const issueCountTotal = critical + high + medium + low
return {
id: project.id,
name: project.attributes.name,
isMonitored:
project.attributes.status === 'active',
issueCountTotal
}
}).filter(({ id, isMonitored, issueCountTotal }) => {
const projects = await Promise.all(
responseData.map(async (project) => {
const { critical, high, medium, low } = project.meta.latest_issue_counts
const issueCountTotal = critical + high + medium + low

const projectDetails = await this.queryProjectDetails(organizationId, project.id)

return {
id: project.id,
name: project.attributes.name,
isMonitored:
project.attributes.status === 'active',
issueCountTotal,
browseUrl: projectDetails.browseUrl,
imageTag: projectDetails.imageTag
}
})
)

return projects.filter(({ id, isMonitored }) => {
if (selectedProjects.includes(id)) {
return true
}
Expand Down Expand Up @@ -99,18 +122,22 @@ function getSeverities (minimumSeverity) {
}

async function paginateRestResponseData (url, headers, method = 'get') {
const reponseData = []
do {
const response = await request({
method,
url,
headers,
json: true
})
reponseData.push(...response.data)
if (response.links.next) url = baseRestUrl + response.links.next
else url = undefined
} while (url)
try {
const reponseData = []
do {
const response = await request({
method,
url,
headers,
json: true
})
reponseData.push(...response.data)
if (response.links.next) url = baseRestUrl + response.links.next.trimStart('/rest')
else url = undefined
} while (url)

return reponseData
return reponseData
} catch (err) {
throw new Error(dressError(err, `Failed to paginate request for ${method} ${url}`))
}
}
7 changes: 6 additions & 1 deletion lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ const capitalize = (s) => {

const uniq = (array) => [...new Set(array)]

const dressError = (err, msg) => {
return `${msg}, error: ${err.status ? err.status + ' - ' : ''}${err.message}, response: ${err.response}`
}

module.exports = {
capitalize,
compare: {
Expand All @@ -82,5 +86,6 @@ module.exports = {
versionArrays: compareVersionArrays,
arrays: compareArrays
},
uniq
uniq,
dressError
}
Loading