Skip to content
This repository was archived by the owner on Apr 20, 2024. It is now read-only.

Commit 906ef4a

Browse files
committed
added source files
1 parent 7ecc669 commit 906ef4a

File tree

3 files changed

+201
-0
lines changed

3 files changed

+201
-0
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import Vapor
2+
import HTTP
3+
4+
public final class BugsnagMiddleware: Middleware {
5+
6+
let drop: Droplet
7+
let configuration: Configuration
8+
let connectionManager: ConnectionMananger
9+
public init(drop: Droplet) throws {
10+
self.drop = drop
11+
self.configuration = try Configuration(drop: drop)
12+
self.connectionManager = ConnectionMananger(drop: drop, config: configuration)
13+
}
14+
15+
public func respond(to request: Request, chainingTo next: Responder) throws -> Response {
16+
17+
do {
18+
return try next.respond(to: request)
19+
} catch Abort.badRequest {
20+
try report(status: .badRequest, message: "Bad request", request: request)
21+
throw Abort.badRequest
22+
} catch Abort.serverError {
23+
try report(status: .internalServerError, message: "Server error", request: request)
24+
throw Abort.serverError
25+
} catch Abort.notFound {
26+
try report(status: .notFound, message: "Not found", request: request)
27+
throw Abort.notFound
28+
} catch Abort.custom(let status, let message) {
29+
try report(status: status, message: message, request: request)
30+
throw Abort.custom(status: status, message: message)
31+
}
32+
// This can be incommented when Vapor updates
33+
/*catch Abort.customWithCode(let status, let message, let code) {
34+
try report(status: status, message: message, request: request)
35+
throw Abort.customWithCode(status: status, message: message, code: code)
36+
}*/
37+
}
38+
39+
public func report(status: Status, message: String, request: Request) throws {
40+
_ = try connectionManager.post(status: status, message: message, request: request)
41+
}
42+
}
43+
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import Foundation
2+
import Vapor
3+
import HTTP
4+
5+
public struct Configuration {
6+
7+
public enum Field: String {
8+
case apiKey = "bugsnag.apiKey"
9+
case notifyReleaseStages = "bugsnag.notifyReleaseStages"
10+
case endpoint = "bugsnag.endpoint"
11+
case filters = "bugsnag.filters"
12+
13+
var path: [String] {
14+
return rawValue.components(separatedBy: ".")
15+
}
16+
17+
var error: Abort {
18+
return .custom(status: .internalServerError,
19+
message: "Bugsnag error - \(rawValue) config is missing.")
20+
}
21+
}
22+
23+
public let apiKey: String
24+
public let notifyReleaseStages: [String]
25+
public let endpoint: String
26+
public let filters: [String]
27+
public init(drop: Droplet) throws {
28+
self.apiKey = try Configuration.extract(field: .apiKey, drop: drop)
29+
self.notifyReleaseStages = try Configuration.extract(field: .notifyReleaseStages, drop: drop)
30+
self.endpoint = try Configuration.extract(field: .endpoint, drop: drop)
31+
self.filters = try Configuration.extract(field: .filters, drop: drop)
32+
}
33+
34+
private static func extract(field: Field , drop: Droplet) throws -> [String] {
35+
// Get array
36+
guard let platforms = drop.config[field.path]?.array else {
37+
throw field.error
38+
}
39+
40+
// Get from config and make sure all values are strings
41+
return try platforms.map({
42+
guard let string = $0.string else {
43+
throw field.error
44+
}
45+
46+
return string
47+
})
48+
}
49+
50+
private static func extract(field: Field , drop: Droplet) throws -> String {
51+
guard let string = drop.config[field.path]?.string else {
52+
throw field.error
53+
}
54+
55+
return string
56+
}
57+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import Vapor
2+
import HTTP
3+
import Foundation
4+
5+
public final class ConnectionMananger {
6+
7+
let drop: Droplet
8+
let config: Configuration
9+
10+
public init(drop: Droplet, config: Configuration) {
11+
self.drop = drop
12+
self.config = config
13+
}
14+
15+
func headers() -> [HeaderKey: String] {
16+
17+
let headers = [
18+
HeaderKey("Content-Type"): "application/json",
19+
]
20+
21+
return headers
22+
}
23+
24+
func body(message: String, request: Request) throws -> JSON {
25+
var code: [String: Node] = [:]
26+
27+
var index = 0
28+
for entry in Thread.callStackSymbols {
29+
code[String(index)] = Node(entry)
30+
31+
index = index + 1
32+
}
33+
34+
let stacktrace = Node([
35+
Node([
36+
"file": Node(message),
37+
"lineNumber": 0,
38+
"columnNumber": 0,
39+
"method": "NA",
40+
"code": Node(code)
41+
])
42+
])
43+
44+
let app: Node = Node([
45+
"releaseStage": Node(drop.environment.description),
46+
"type": "Vapor"
47+
])
48+
49+
var headers: [String: Node] = [:]
50+
for (key, value) in request.headers {
51+
headers[key.key] = Node(value)
52+
}
53+
54+
let metaData = Node([
55+
"request": Node([
56+
"method": Node(request.method.description),
57+
"headers": Node(headers),
58+
"params": request.parameters,
59+
"url": Node(request.uri.path)
60+
])
61+
])
62+
63+
64+
let event: Node = Node([
65+
Node([
66+
"payloadVersion": 2,
67+
"exceptions": Node([
68+
Node([
69+
"errorClass": Node(message),
70+
"message": Node(message),
71+
"stacktrace": stacktrace
72+
])
73+
]),
74+
"app": app,
75+
"severity": "error",
76+
"metaData": metaData
77+
])
78+
])
79+
80+
return try JSON(node: [
81+
"apiKey": self.config.apiKey,
82+
"notifier": Node([
83+
"name": "Bugsnag Vapor",
84+
"version": "1.0.11",
85+
"url": "https://github.com/nodes-vapor/bugsnag"
86+
]),
87+
"events": event,
88+
])
89+
}
90+
91+
func post(json: JSON) throws -> Status {
92+
let response = try drop.client.post(self.config.endpoint, headers: headers(), body: json.makeBody())
93+
94+
return response.status
95+
}
96+
97+
func post(status: Status, message: String, request: Request) throws -> Status {
98+
return try post(json: body(message: message, request: request))
99+
}
100+
101+
}

0 commit comments

Comments
 (0)