Skip to content

Commit

Permalink
Implement sources in diagnose report
Browse files Browse the repository at this point in the history
This commit implements the `.config.sources` attribute of the
diagnose report.
  • Loading branch information
unflxw committed Nov 5, 2021
1 parent 4711784 commit 3a3dca3
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 43 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
bump: "patch"
---

Include the sources of each configuration value in the diagnose report that is sent to AppSignal.com.
122 changes: 87 additions & 35 deletions packages/nodejs/src/__tests__/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,62 @@ describe("Configuration", () => {
const apiKey = "TEST_API_KEY"

let config: Configuration
let initialEnv: { [key: string]: any }

beforeEach(() => {
jest.resetModules()
config = new Configuration({ name, apiKey })
})
function resetEnv() {
Object.keys(process.env).forEach(key => {
if (!initialEnv.hasOwnProperty(key)) {
delete process.env[key]
}
})
}

it("writes key and name to the environment", () => {
expect(process.env["_APPSIGNAL_APP_NAME"]).toEqual(name)
expect(process.env["_APPSIGNAL_PUSH_API_KEY"]).toEqual(apiKey)
beforeAll(() => {
initialEnv = Object.assign({}, process.env)
})

it("writes private constants to the environment", () => {
expect(process.env["_APPSIGNAL_AGENT_PATH"]).toBeDefined()
expect(process.env["_APPSIGNAL_ENVIRONMENT"]).toBeDefined()
expect(process.env["_APPSIGNAL_PROCESS_NAME"]).toBeDefined()
expect(process.env["_APPSIGNAL_LANGUAGE_INTEGRATION_VERSION"]).toBeDefined()
expect(process.env["_APPSIGNAL_APP_PATH"]).toBeDefined()
afterAll(() => {
resetEnv()
})

it("recognises a valid configuration", () => {
expect(config.isValid).toBeTruthy()
beforeEach(() => {
jest.resetModules()
resetEnv()
config = new Configuration({ name, apiKey })
})

it("loads configuration from the environment", () => {
process.env["APPSIGNAL_ACTIVE"] = "true"
describe("Private environment variables", () => {
it("writes initial values to the environment", () => {
expect(process.env["_APPSIGNAL_APP_NAME"]).toEqual(name)
expect(process.env["_APPSIGNAL_PUSH_API_KEY"]).toEqual(apiKey)
})

config = new Configuration({ name, apiKey })
expect(process.env["_APPSIGNAL_ACTIVE"]).toBeTruthy()
})
it("writes private constants to the environment", () => {
expect(process.env["_APPSIGNAL_AGENT_PATH"]).toBeDefined()
expect(process.env["_APPSIGNAL_ENVIRONMENT"]).toBeDefined()
expect(process.env["_APPSIGNAL_PROCESS_NAME"]).toBeDefined()
expect(
process.env["_APPSIGNAL_LANGUAGE_INTEGRATION_VERSION"]
).toBeDefined()
expect(process.env["_APPSIGNAL_APP_PATH"]).toBeDefined()
})

it("uses a default log file path", () => {
expect(process.env["_APPSIGNAL_LOG_FILE_PATH"]).toEqual(
"/tmp/appsignal.log"
)
it("loads configuration from the environment", () => {
process.env["APPSIGNAL_ACTIVE"] = "true"

config = new Configuration({ name, apiKey })
expect(process.env["_APPSIGNAL_ACTIVE"]).toBeTruthy()
})

it("uses a default log file path", () => {
expect(process.env["_APPSIGNAL_LOG_FILE_PATH"]).toEqual(
"/tmp/appsignal.log"
)
})
})

describe("Default options", () => {
const expectedConfig = {
const expectedDefaultConfig = {
caFilePath: path.join(__dirname, "../../cert/cacert.pem"),
debug: false,
dnsServers: [],
Expand All @@ -64,25 +82,59 @@ describe("Configuration", () => {
transactionDebugMode: false
}

const expectedInitialConfig = {
name,
apiKey
}

const expectedConfig = {
...expectedDefaultConfig,
...expectedInitialConfig
}

it("loads all default options when no options are overwritten", () => {
expect(config.data).toMatchObject(expectedConfig)
expect(config.data).toEqual(expectedConfig)

expect(config.sources.default).toEqual(expectedDefaultConfig)
expect(config.sources.initial).toEqual(expectedInitialConfig)
expect(config.sources.env).toEqual({})
})

it("loads default options and overwrites the specified ones", () => {
const expectedOverwrittenConfig = {
...expectedConfig,
describe("overwrites default values", () => {
const overwrittenConfig = {
debug: true,
enableStatsd: true
}

config = new Configuration({
name,
apiKey,
debug: true,
enableStatsd: true
const expectedConfig = {
...expectedDefaultConfig,
...overwrittenConfig
}

it("with initial values", () => {
config = new Configuration({
...overwrittenConfig
})

expect(config.data).toEqual(expectedConfig)

expect(config.sources.default).toEqual(expectedDefaultConfig)
expect(config.sources.initial).toEqual(overwrittenConfig)
expect(config.sources.env).toEqual({})
})

expect(config.data).toMatchObject(expectedOverwrittenConfig)
it("with environment values", () => {
process.env["APPSIGNAL_DEBUG"] = "true"
process.env["APPSIGNAL_ENABLE_STATSD"] = "true"

config = new Configuration({})

expect(config.data).toEqual(expectedConfig)

expect(config.sources.default).toEqual(expectedDefaultConfig)
expect(config.sources.initial).toEqual({})
expect(config.sources.env).toEqual(overwrittenConfig)
})
})
})

Expand Down
14 changes: 10 additions & 4 deletions packages/nodejs/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import path from "path"
import { VERSION } from "./version"
import { AppsignalOptions } from "./interfaces/options"
import { ENV_TO_KEY_MAPPING, PRIVATE_ENV_MAPPING } from "./config/configmap"
import { HashMap } from "@appsignal/types"

/**
* The AppSignal configuration object.
Expand All @@ -14,16 +15,21 @@ import { ENV_TO_KEY_MAPPING, PRIVATE_ENV_MAPPING } from "./config/configmap"
*/
export class Configuration {
data: Partial<AppsignalOptions>
sources: HashMap<Partial<AppsignalOptions>>

constructor(options: Partial<AppsignalOptions>) {
writePrivateConstants()

this.data = {
...this._defaultValues(),
...this._loadFromEnvironment(),
...options
this.sources = {
default: this._defaultValues(),
env: this._loadFromEnvironment(),
initial: options
}

this.data = Object.values(this.sources).reduce((data, options) => {
return {...data, ...options}
}, {});

this._write(this.data)
}

Expand Down
28 changes: 24 additions & 4 deletions packages/nodejs/src/diagnose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Extension } from "./extension"
import { Configuration } from "./config"
import { AGENT_VERSION, VERSION } from "./version"
import { JS_TO_RUBY_MAPPING } from "./config/configmap"
import { AppsignalOptions } from "."

interface FileMetadata {
content?: string[]
Expand Down Expand Up @@ -54,7 +55,7 @@ export class DiagnoseTool {
agent: this.#extension.diagnose(),
config: {
options: this.getConfigData(),
sources: {}
sources: this.getSources()
},
validation: { push_api_key: pushApiKeyValidation },
process: {
Expand Down Expand Up @@ -182,19 +183,38 @@ export class DiagnoseTool {

/**
* Reads all configuration and re-maps it to keys with
* snake_case names as they appear in our API.
* snake_case names.
*/
private getConfigData() {
return this.optionsObject(this.#config.data)
}

/**
* Converts an AppsignalOptions object into a plain JS object,
* re-mapping its keys to snake_case names as they appear
* in our API.
*/
private optionsObject(options: Partial<AppsignalOptions>) {
const config: { [key: string]: any } = {}

Object.keys(this.#config.data).forEach(key => {
Object.keys(options).forEach(key => {
const newKey = JS_TO_RUBY_MAPPING[key]
config[newKey] = this.#config.data[key]
config[newKey] = options[key]
})

return config
}

/**
* Reads all configuration sources, remapping each source's
* option keys with snake_case names.
*/
private getSources() {
return Object.entries(this.#config.sources).reduce((sources, [name, options]) => {
return {...sources, [name]: this.optionsObject(options)}
}, {})
}

public sendReport(data: object) {
const json = JSON.stringify({ diagnose: data })

Expand Down

0 comments on commit 3a3dca3

Please sign in to comment.