Skip to content
This repository has been archived by the owner on Dec 15, 2022. It is now read-only.

Payload reduction for issue creation #141

Merged
merged 6 commits into from
Jan 9, 2017
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
14 changes: 10 additions & 4 deletions lib/notification-element.coffee
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
fs = require 'fs-plus'
path = require 'path'
marked = require 'marked'
{shell} = require 'electron'

NotificationIssue = require './notification-issue'
TemplateHelper = require './template-helper'
Expand Down Expand Up @@ -168,12 +169,11 @@ class NotificationElement extends HTMLElement

promises = []
promises.push @issue.findSimilarIssues()
promises.push @issue.getIssueUrlForSystem()
promises.push UserUtilities.checkAtomUpToDate()
promises.push UserUtilities.checkPackageUpToDate(packageName) if packageName?

Promise.all(promises).then (allData) ->
[issues, newIssueUrl, atomCheck, packageCheck] = allData
Promise.all(promises).then (allData) =>
[issues, atomCheck, packageCheck] = allData

if issues?.open or issues?.closed
issue = issues.open or issues.closed
Expand Down Expand Up @@ -218,8 +218,14 @@ class NotificationElement extends HTMLElement
Upgrading to the <a href='https://github.com/atom/atom/releases/tag/v#{atomCheck.latestVersion}'>latest version</a> may fix this issue.
"""
else
issueButton.setAttribute('href', newIssueUrl) if newIssueUrl?
fatalNotification.innerHTML += " You can help by creating an issue. Please explain what actions triggered this error."
issueButton.addEventListener 'click', (e) =>
e.preventDefault()
issueButton.classList.add('opening')
@issue.getIssueUrlForSystem().then (issueUrl) ->
shell.openExternal(issueUrl)
issueButton.classList.remove('opening')

return
else
Promise.resolve()
Expand Down
47 changes: 21 additions & 26 deletions lib/notification-issue.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ class NotificationIssue
query = "#{issueTitle} repo:#{repo}"

fetch "https://api.github.com/search/issues?q=#{encodeURIComponent(query)}&sort=created", {headers: githubHeaders}
.then (r) => r?.json()
.then (data) =>
.then (r) -> r?.json()
.then (data) ->
if data?.items?
issues = {}
for issue in data.items
Expand All @@ -40,10 +40,13 @@ class NotificationIssue
.catch (e) -> null

getIssueUrlForSystem: ->
# Windows will not launch URLs greater than ~2000 bytes so we need to shrink it
# Also is.gd has a limit of 5000 bytes...
@getIssueUrl().then (issueUrl) ->
fetch "https://is.gd/create.php?format=simple", {
method: 'POST',
body: "url=#{encodeURI(issueUrl)}"
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: "url=#{encodeURIComponent(issueUrl)}"
}
.then (r) -> r.text()
.catch (e) -> null
Expand All @@ -52,7 +55,10 @@ class NotificationIssue
@getIssueBody().then (issueBody) =>
repoUrl = @getRepoUrl()
repoUrl = 'https://github.com/atom/atom' unless repoUrl?
"#{repoUrl}/issues/new?title=#{encodeURIComponent(@getIssueTitle())}&body=#{encodeURIComponent(issueBody)}"
"#{repoUrl}/issues/new?title=#{@encodeURI(@getIssueTitle())}&body=#{@encodeURI(issueBody)}"

encodeURI: (str) ->
encodeURI(str).replace(/#/g, '%23').replace(/;/g, '%3B').replace(/%20/g, '+')

getIssueTitle: ->
title = @notification.getMessage()
Expand All @@ -71,17 +77,16 @@ class NotificationIssue
new Promise (resolve, reject) =>
return resolve(@issueBody) if @issueBody
systemPromise = UserUtilities.getOSVersion()
installedPackagesPromise = UserUtilities.getInstalledPackages()
nonCorePackagesPromise = UserUtilities.getNonCorePackages()

Promise.all([systemPromise, installedPackagesPromise]).then (all) =>
[systemName, installedPackages] = all
Promise.all([systemPromise, nonCorePackagesPromise]).then (all) =>
[systemName, nonCorePackages] = all

message = @notification.getMessage()
options = @notification.getOptions()
repoUrl = @getRepoUrl()
packageName = @getPackageName()
packageVersion = atom.packages.getLoadedPackage(packageName)?.metadata?.version if packageName?
userConfig = UserUtilities.getConfigForPackage(packageName)
copyText = ''
systemUser = process.env.USER
rootUserStatus = ''
Expand All @@ -90,24 +95,24 @@ class NotificationIssue
rootUserStatus = '**User**: root'

if packageName? and repoUrl?
packageMessage = "[#{packageName}](#{repoUrl}) package, v#{packageVersion}"
packageMessage = "[#{packageName}](#{repoUrl}) package #{packageVersion}"
else if packageName?
packageMessage = "'#{packageName}' package, v#{packageVersion}"
packageMessage = "'#{packageName}' package v#{packageVersion}"
else
packageMessage = 'Atom Core'

atomVersion = atom.getVersion()
electronVersion = process.versions.electron

@issueBody = """
[Enter steps to reproduce below:]
[Enter steps to reproduce:]

1. ...
2. ...

**Atom Version**: #{atomVersion}
**Electron Version**: #{electronVersion}
**System**: #{systemName}
**Atom**: #{atomVersion}
**Electron**: #{electronVersion}
**OS**: #{systemName}
**Thrown From**: #{packageMessage}
#{rootUserStatus}

Expand All @@ -125,20 +130,10 @@ class NotificationIssue

#{CommandLogger.instance().getText()}

### Config
### Non-Core Packages

```json
#{JSON.stringify(userConfig, null, 2)}
```

### Installed Packages

```coffee
# User
#{installedPackages.user.join('\n') or 'No installed packages'}

# Dev
#{installedPackages.dev.join('\n') or 'No dev packages'}
#{nonCorePackages.join('\n')}
```

#{copyText}
Expand Down
48 changes: 8 additions & 40 deletions lib/user-utilities.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ module.exports =

macVersionText: ->
@macVersionInfo().then (info) ->
return 'Unknown OS X version' unless info.ProductName and info.ProductVersion
return 'Unknown macOS version' unless info.ProductName and info.ProductVersion
"#{info.ProductName} #{info.ProductVersion}"

macVersionInfo: ->
Expand Down Expand Up @@ -91,54 +91,22 @@ module.exports =
stdout: (oneLine) -> data.push(oneLine)
exit: ->
info = data.join('\n')
info = if (res = /OS.Name.\s+(.*)$/im.exec(info)) then res[1] else 'Unknown Windows Version'
info = if (res = /OS.Name.\s+(.*)$/im.exec(info)) then res[1] else 'Unknown Windows version'
resolve(info)

systemInfo.onWillThrowError ({handle}) ->
handle()
resolve('Unknown Windows Version')

###
Section: Config Values
###

getConfigForPackage: (packageName) ->
config = core: atom.config.settings.core
if packageName?
config[packageName] = atom.config.settings[packageName]
else
config.editor = atom.config.settings.editor
config
resolve('Unknown Windows version')

###
Section: Installed Packages
###

isDevModePackagePath: (packagePath) ->
packagePath.match(DEV_PACKAGE_PATH)?

# Returns a promise. Resolves with object of arrays {dev: ['some-package, v0.2.3', ...], user: [...]}
getInstalledPackages: ->
new Promise (resolve, reject) =>
devPackagePaths = atom.packages.getAvailablePackagePaths().filter(@isDevModePackagePath)
devPackageNames = devPackagePaths.map((packagePath) -> path.basename(packagePath))
availablePackages = atom.packages.getAvailablePackageMetadata()
activePackageNames = atom.packages.getActivePackages().map((activePackage) -> activePackage.name)
resolve
dev: @getPackageNames(availablePackages, devPackageNames, activePackageNames, true)
user: @getPackageNames(availablePackages, devPackageNames, activePackageNames, false)

getActiveLabel: (packageName, activePackageNames) ->
if packageName in activePackageNames
'active'
else
'inactive'

getPackageNames: (availablePackages, devPackageNames, activePackageNames, devMode) ->
if devMode
"#{pack.name}, v#{pack.version} (#{@getActiveLabel(pack.name, activePackageNames)})" for pack in (availablePackages ? []) when pack.name in devPackageNames
else
"#{pack.name}, v#{pack.version} (#{@getActiveLabel(pack.name, activePackageNames)})" for pack in (availablePackages ? []) when pack.name not in devPackageNames
getNonCorePackages: ->
new Promise (resolve, reject) ->
nonCorePackages = atom.packages.getAvailablePackageMetadata().filter((p) -> not atom.packages.isBundledPackage(p.name))
devPackageNames = atom.packages.getAvailablePackagePaths().filter((p) -> p.includes(DEV_PACKAGE_PATH)).map((p) -> path.basename(p))
resolve("#{pack.name} #{pack.version} #{if pack.name in devPackageNames then '(dev)' else ''}" for pack in nonCorePackages)

getLatestAtomData: ->
fetch 'https://atom.io/api/updates', {headers: githubHeaders}
Expand Down
38 changes: 11 additions & 27 deletions spec/notifications-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,8 @@ describe "Notifications", ->
waitsForPromise ->
fatalError.getRenderPromise().then ->
issueTitle = fatalError.issue.getIssueTitle()
issueBody = fatalError.issue.issueBody
fatalError.issue.getIssueBody().then (result) ->
issueBody = result

runs ->
expect(notificationContainer.childNodes.length).toBe 1
Expand All @@ -314,28 +315,17 @@ describe "Notifications", ->

button = fatalError.querySelector('.btn')
expect(button.textContent).toContain 'Create issue on the notifications package'
expect(button.getAttribute('href')).toContain 'is.gd/cats'

expect(issueTitle).toContain '$ATOM_HOME'
expect(issueTitle).not.toContain process.env.ATOM_HOME
expect(issueBody).toMatch /Atom Version\*\*: [0-9].[0-9]+.[0-9]+/ig
expect(issueBody).toMatch /Atom\*\*: [0-9].[0-9]+.[0-9]+/ig
expect(issueBody).not.toMatch /Unknown/ig
expect(issueBody).toContain 'ReferenceError: a is not defined'
expect(issueBody).toContain 'Thrown From**: [notifications](https://github.com/atom/notifications) package, v'
expect(issueBody).toContain '# User'
expect(issueBody).toContain 'Thrown From**: [notifications](https://github.com/atom/notifications) package '
expect(issueBody).toContain '### Non-Core Packages'

# FIXME: this doesnt work on the test server. `apm ls` is not working for some reason.
# expect(issueBody).toContain 'notifications, v'

it "contains core and notifications config values", ->
atom.config.set('notifications.something', 10)
waitsForPromise ->
fatalError.getRenderPromise().then -> issueBody = fatalError.issue.issueBody

runs ->
expect(issueBody).toContain '"core":'
expect(issueBody).toContain '"notifications":'
expect(issueBody).not.toContain '"editor":'
# expect(issueBody).toContain 'notifications '

it "standardizes platform separators on #win32", ->
waitsForPromise ->
Expand Down Expand Up @@ -609,7 +599,9 @@ describe "Notifications", ->
notificationContainer = workspaceElement.querySelector('atom-notifications')
fatalError = notificationContainer.querySelector('atom-notification.fatal')
waitsForPromise ->
fatalError.getRenderPromise().then -> issueBody = fatalError.issue.issueBody
fatalError.getRenderPromise().then ->
fatalError.issue.getIssueBody().then (result) ->
issueBody = result

it "displays a fatal error with the package name in the error", ->
expect(notificationContainer.childNodes.length).toBe 1
Expand All @@ -621,16 +613,10 @@ describe "Notifications", ->

button = fatalError.querySelector('.btn')
expect(button.textContent).toContain 'Create issue on atom/atom'
expect(button.getAttribute('href')).toContain 'is.gd/cats'

expect(issueBody).toContain 'ReferenceError: a is not defined'
expect(issueBody).toContain '**Thrown From**: Atom Core'

it "contains core and editor config values", ->
expect(issueBody).toContain '"core":'
expect(issueBody).toContain '"editor":'
expect(issueBody).not.toContain '"notifications":'

it "contains the commands that the user run in the issue body", ->
expect(issueBody).toContain 'some-package:a-command'

Expand Down Expand Up @@ -660,7 +646,6 @@ describe "Notifications", ->
fatalNotification = fatalError.querySelector('.fatal-notification')
expect(button.textContent).toContain 'Create issue'
expect(fatalNotification.textContent).toContain 'You can help by creating an issue'
expect(button.getAttribute('href')).toContain 'is.gd/cats'

describe "when the error has not been reported", ->
beforeEach ->
Expand Down Expand Up @@ -689,7 +674,6 @@ describe "Notifications", ->
button = fatalError.querySelector('.btn')
encodedMessage = encodeURIComponent(truncatedMessage)
expect(button.textContent).toContain 'Create issue'
expect(button.getAttribute('href')).toContain 'is.gd/cats'

describe "when the package is out of date", ->
beforeEach ->
Expand Down Expand Up @@ -911,8 +895,8 @@ generateException = ->
# shortenerResponse
# packageResponse
# issuesResponse
generateFakeFetchResponses = (options) =>
fetch.andCallFake (url) =>
generateFakeFetchResponses = (options) ->
fetch.andCallFake (url) ->
if url.indexOf('is.gd') > -1
return textPromise options?.shortenerResponse ? 'http://is.gd/cats'

Expand Down
4 changes: 4 additions & 0 deletions styles/notifications.less
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,10 @@ atom-notification {
.btn-copy-report {
vertical-align: middle;
}

.opening {
cursor: progress;
}
}

// Types -------------------------------
Expand Down