Skip to content

Commit

Permalink
Create a single transmitter for Diagnose
Browse files Browse the repository at this point in the history
Diagnose script performs two external HTTP calls, one for push api key
validation, and another one to send the diagnose results to AppSignal if
the user wants.

These calls were written separatedly inside the diagnose script, now
they are done through a new class called `Transmitter`.
  • Loading branch information
luismiramirez committed Dec 16, 2021
1 parent 48589ca commit 95dd168
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 77 deletions.
10 changes: 5 additions & 5 deletions packages/nodejs/src/cli/diagnose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ export class Diagnose {
` Not sending report. (Specified with the --no-send-report option.)`
)
} else if (process.argv.includes("--send-report")) {
this.sendReport(data)
await this.sendReport(data)
} else {
const rl = readline.createInterface({
input: process.stdin,
Expand All @@ -219,10 +219,10 @@ export class Diagnose {
let self = this
rl.question(
` Send diagnostics report to AppSignal? (Y/n): `,
function (answer: String) {
async function (answer: String) {
switch (answer || "y") {
case "y":
self.sendReport(data)
await self.sendReport(data)
break

default:
Expand All @@ -235,10 +235,10 @@ export class Diagnose {
}
}

sendReport(data: object) {
async sendReport(data: object) {
console.log(" Transmitting diagnostics report")
console.log("")
this.#diagnose.sendReport(data)
await this.#diagnose.sendReport(data)
}

printAgentDiagnose(report: HashMap<any>) {
Expand Down
110 changes: 38 additions & 72 deletions packages/nodejs/src/diagnose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { AGENT_VERSION, VERSION } from "./version"
import { JS_TO_RUBY_MAPPING } from "./config/configmap"
import { AppsignalOptions } from "."
import { HashMap } from "@appsignal/types"
import { Transmitter } from "./transmitter"

interface FileMetadata {
content?: string[]
Expand Down Expand Up @@ -112,31 +113,27 @@ export class DiagnoseTool {
}

private async validatePushApiKey() {
return new Promise((resolve, reject) => {
const config = this.#config.data
const params = new URLSearchParams({
api_key: config["pushApiKey"] || "",
name: config["name"] || "",
environment: config["environment"] || "",
hostname: config["hostname"] || ""
const config = this.#config.data
const url = new URL(`/1/auth`, config["endpoint"])
const transmitter = new Transmitter(url.toString())
let statusCode = null

await transmitter
.transmit()
.then(responseData => {
statusCode = responseData["status"]
})
const url = new URL(`/1/auth?${params.toString()}`, config["endpoint"])
const options = { method: "POST" }

const requestModule = url.protocol == "http:" ? http : https
const request = requestModule.request(url, options, function (response) {
const status = response.statusCode
if (status === 200) {
resolve("valid")
} else if (status === 401) {
reject("invalid")
} else {
reject(`Failed to validate: status ${status}`)
}
.catch(responseData => {
statusCode = responseData["status"]
})
request.write("") // Send empty body
request.end()
})

if (statusCode == 200) {
return Promise.resolve("valid")
} else if (statusCode == 401) {
return Promise.reject("invalid")
} else {
return Promise.reject(`Failed to validate: status ${statusCode}`)
}
}

private getPathsData() {
Expand Down Expand Up @@ -232,63 +229,32 @@ export class DiagnoseTool {
)
}

public sendReport(data: HashMap<any>) {
public async sendReport(data: HashMap<any>) {
data.config.options = this.getConfigData()
data.config.sources = this.getSources()
const json = JSON.stringify({ diagnose: data })

const config = this.#config.data
const params = new URLSearchParams({
api_key: config["pushApiKey"] || "",
name: config["name"] || "",
environment: config["environment"] || "",
hostname: config["hostname"] || ""
})

const diagnoseEndpoint =
process.env.APPSIGNAL_DIAGNOSE_ENDPOINT || "https://appsignal.com/diag"
const url = new URL(diagnoseEndpoint)

const opts = {
method: "POST",
protocol: url.protocol,
host: url.hostname,
port: url.port,
path: `${url.pathname}?${params.toString()}`,
headers: {
"Content-Type": "application/json",
"Content-Length": json.length
},
cert: fs.readFileSync(
path.resolve(__dirname, "../cert/cacert.pem"),
"utf-8"
)
}

const requestModule = url.protocol == "http:" ? http : https
const request = requestModule.request(opts, (response: any) => {
const responseStatus = response.statusCode
response.setEncoding("utf8")

response.on("data", (responseData: any) => {
if (responseStatus === 200) {
const { token } = JSON.parse(responseData.toString())
console.log(` Your support token:`, token)
console.log(
` View this report: https://appsignal.com/diagnose/${token}`
)
} else {
console.error(
" Error: Something went wrong while submitting the report to AppSignal."
)
console.error(` Response code: ${responseStatus}`)
console.error(` Response body:\n${responseData}`)
}
})
})
const transmitter = new Transmitter(diagnoseEndpoint, json)

request.write(json)
request.end()
await transmitter
.transmit()
.then(responseData => {
const { token } = responseData["body"]
console.log(` Your support token:`, token)
console.log(
` View this report: https://appsignal.com/diagnose/${token}`
)
})
.catch(responseData => {
console.error(
" Error: Something went wrong while submitting the report to AppSignal."
)
console.error(` Response code: ${responseData["status"]}`)
console.error(` Response body:\n${responseData["body"]}`)
})
}
}

Expand Down
84 changes: 84 additions & 0 deletions packages/nodejs/src/transmitter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import fs from "fs"
import path from "path"
import https from "https"
import http from "http"

import { Configuration } from "./config"
import { URL, URLSearchParams } from "url"
import { HashMap } from "@appsignal/types"

export class Transmitter {
#config: Configuration
#url: string
#data: string

constructor(url: string, data = "", active = true) {
this.#config = new Configuration({ active })
this.#url = url
this.#data = data
}

public async transmit() {
return new Promise<HashMap<any>>((resolve, reject) => {
const config_data = this.#config.data
const params = new URLSearchParams({
api_key: config_data["pushApiKey"] || "",
name: config_data["name"] || "",
environment: config_data["environment"] || "",
hostname: config_data["hostname"] || ""
})

const url = new URL(`${this.#url}?${params.toString()}`)
const opts = this.requestOptions(url)
const requestModule = url.protocol == "http:" ? http : https

const request = requestModule.request(opts, (stream: any) => {
const responseStatus = stream.statusCode
stream.setEncoding("utf8")
let responseBody = ""

stream
.on("data", (chunk: any) => {
responseBody += chunk
})
.on("end", () => {
let parsedResponse
try {
parsedResponse = JSON.parse(responseBody)
} catch {
parsedResponse = {}
}

if (responseStatus == 200) {
resolve({ status: responseStatus, body: parsedResponse })
} else {
reject({ status: responseStatus, body: parsedResponse })
}
})
})

request.write(this.#data)
request.end()
})
}

private requestOptions(requestUrl: URL): HashMap<any> {
const configData = this.#config.data

return {
method: "POST",
protocol: requestUrl.protocol,
host: requestUrl.hostname,
port: requestUrl.port,
path: requestUrl.toString(),
headers: {
"Content-Type": "application/json",
"Content-Length": this.#data.length
},
cert: fs.readFileSync(
path.resolve(__dirname, "../cert/cacert.pem"),
"utf-8"
)
}
}
}

0 comments on commit 95dd168

Please sign in to comment.