Compare commits

..

No commits in common. "develop" and "v1.0.4" have entirely different histories.

12 changed files with 3585 additions and 5549 deletions

1
.envrc
View file

@ -1 +0,0 @@
use flake

1
.github/FUNDING.yml vendored
View file

@ -1,2 +1 @@
github: lstrojny github: lstrojny
custom: ["https://paypal.me/larsstrojny"]

View file

@ -20,16 +20,16 @@ jobs:
name: nodejs ${{ matrix.node-version }} (${{ matrix.lint && 'lint → ' || '' }}${{ matrix.tests && 'test → ' || '' }}build) name: nodejs ${{ matrix.node-version }} (${{ matrix.lint && 'lint → ' || '' }}${{ matrix.tests && 'test → ' || '' }}build)
steps: steps:
- uses: actions/checkout@v4.1.0 - uses: actions/checkout@v3.3.0
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.8.1 uses: actions/setup-node@v3.6.0
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
- name: Cache node modules - name: Cache node modules
id: cache-npm id: cache-npm
uses: actions/cache@v3.3.2 uses: actions/cache@v3.2.2
env: env:
cache-name: cache-node-modules cache-name: cache-node-modules
with: with:
@ -38,7 +38,7 @@ jobs:
- name: Cache eslint - name: Cache eslint
id: cache-eslint id: cache-eslint
uses: actions/cache@v3.3.2 uses: actions/cache@v3.2.2
env: env:
cache-name: cache-eslint cache-name: cache-eslint
with: with:
@ -47,7 +47,7 @@ jobs:
- name: Cache TypeScript - name: Cache TypeScript
id: cache-typescript id: cache-typescript
uses: actions/cache@v3.3.2 uses: actions/cache@v3.2.2
env: env:
cache-name: cache-typescript cache-name: cache-typescript
with: with:
@ -56,7 +56,7 @@ jobs:
- name: Cache prettier - name: Cache prettier
id: cache-prettier id: cache-prettier
uses: actions/cache@v3.3.2 uses: actions/cache@v3.2.2
env: env:
cache-name: cache-prettier cache-name: cache-prettier
with: with:
@ -76,7 +76,7 @@ jobs:
if: ${{ matrix.tests }} if: ${{ matrix.tests }}
- name: Upload code coverage - name: Upload code coverage
uses: actions/upload-artifact@v3.1.3 uses: actions/upload-artifact@v3.1.1
with: with:
name: code-coverage name: code-coverage
path: coverage/lcov.info path: coverage/lcov.info

View file

@ -1,28 +0,0 @@
name: Dependabot auto merge
on:
workflow_run:
workflows: [CI]
types:
- completed
jobs:
automerge:
name: Auto merge "${{ github.event.workflow_run.head_branch }}"
runs-on: ubuntu-22.04
if: >
github.event.workflow_run.event == 'pull_request'
&& github.event.workflow_run.conclusion == 'success'
&& github.actor == 'dependabot[bot]'
&& startsWith(github.event.workflow_run.head_branch, 'dependabot/')
steps:
- name: Checkout source
uses: actions/checkout@v4.1.0
with:
ref: ${{ github.event.workflow_run.head_commit.id }}
- name: Instruct @dependabot to merge
run: "gh issue comment $ISSUE_ID --body \"(This is an automated comment from workflow $WORKFLOW_URL)\n\n@dependabot squash and merge\""
env:
GITHUB_TOKEN: ${{ secrets.DEPENDABOT_COMMENT_TOKEN }}
ISSUE_ID: ${{ github.event.workflow_run.pull_requests[0].number }}
WORKFLOW_URL: ${{ github.event.repository.html_url }}/actions/runs/${{ github.run_id }}

View file

@ -1,4 +1,4 @@
name: Sonar scan name: Sonar
on: on:
workflow_run: workflow_run:
@ -7,12 +7,12 @@ on:
jobs: jobs:
sonar: sonar:
name: Sonar scan on "${{ github.event.workflow_run.head_branch }}" name: Sonar
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event.workflow_run.conclusion == 'success' if: github.event.workflow_run.conclusion == 'success'
steps: steps:
- uses: actions/checkout@v4.1.0 - uses: actions/checkout@v3.3.0
with: with:
repository: ${{ github.event.workflow_run.head_repository.full_name }} repository: ${{ github.event.workflow_run.head_repository.full_name }}
ref: ${{ github.event.workflow_run.head_branch }} ref: ${{ github.event.workflow_run.head_branch }}

9017
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{ {
"name": "homebridge-prometheus-exporter", "name": "homebridge-prometheus-exporter",
"version": "1.0.5", "version": "1.0.4",
"description": "Prometheus exporter for homebridge accessories.", "description": "Prometheus exporter for homebridge accessories.",
"license": "Apache-2.0", "license": "Apache-2.0",
"repository": { "repository": {
@ -35,11 +35,10 @@
"devDependencies": { "devDependencies": {
"@jest/globals": "^29.3.0", "@jest/globals": "^29.3.0",
"@types/bcrypt": "^5.0.0", "@types/bcrypt": "^5.0.0",
"@types/node": "^20.2.3", "@types/node": "^18.11.9",
"@types/supertest": "^2.0.12", "@types/supertest": "^2.0.12",
"@typescript-eslint/eslint-plugin": "^5.42.0", "@typescript-eslint/eslint-plugin": "^5.42.0",
"@typescript-eslint/parser": "^5.42.0", "@typescript-eslint/parser": "^5.42.0",
"array.prototype.flatmap": "^1.3.1",
"eslint": "^8.0.1", "eslint": "^8.0.1",
"eslint-import-resolver-typescript": "^3.5.2", "eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-import": "^2.26.0", "eslint-plugin-import": "^2.26.0",
@ -49,14 +48,14 @@
"homebridge-cmdswitch2": "^0.2.10", "homebridge-cmdswitch2": "^0.2.10",
"jest": "^29.3.0", "jest": "^29.3.0",
"json-schema-to-zod": "^0.6.0", "json-schema-to-zod": "^0.6.0",
"nodemon": "^3.0.1", "nodemon": "^2.0.13",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"release-it": "^16.0.0", "release-it": "^15.5.0",
"rimraf": "^5.0.0", "rimraf": "^3.0.2",
"supertest": "^6.3.1", "supertest": "^6.3.1",
"ts-jest": "^29.0.3", "ts-jest": "^29.0.3",
"ts-node": "^10.3.0", "ts-node": "^10.3.0",
"typescript": "^5.0.2" "typescript": "^4.4.4"
}, },
"dependencies": { "dependencies": {
"@fastify/auth": "^4.1.0", "@fastify/auth": "^4.1.0",

5
src/ambient.d.ts vendored
View file

@ -14,6 +14,11 @@ declare module 'hap-node-client' {
} }
} }
// Workaround for "node_modules/hap-nodejs/dist/lib/Advertiser.d.ts:5:29 - error TS7016: Could not find a declaration file for module '@homebridge/dbus-native'. '…/node_modules/@homebridge/dbus-native/index.js' implicitly has an 'any' type."
declare module '@homebridge/dbus-native' {
type InvokeError = unknown
}
declare module 'array.prototype.group' { declare module 'array.prototype.group' {
function shim(): void function shim(): void
} }

View file

@ -13,16 +13,14 @@ export class MetricsRenderer {
} }
render(metrics: Metric[]): string { render(metrics: Metric[]): string {
return ( return Object.entries(metrics.sort().group((metric) => this.metricName(metric.name)))
Object.entries(metrics.sort().group((metric) => this.metricName(metric.name))) .map(([name, metrics]) => {
.map(([name, metrics]) => { return [
return [ `# TYPE ${name} ${name.endsWith('_total') ? 'counter' : 'gauge'}`,
`# TYPE ${name} ${name.endsWith('_total') ? 'counter' : 'gauge'}`, metrics.map((metric) => this.formatMetric(metric)).join('\n'),
metrics.map((metric) => this.formatMetric(metric)).join('\n'), ].join('\n')
].join('\n') })
}) .join('\n\n')
.join('\n\n') + '\n'
)
} }
private formatMetric(metric: Metric): string { private formatMetric(metric: Metric): string {

View file

@ -74,7 +74,6 @@ describe('Fastify HTTP adapter', () => {
'', '',
'# TYPE homebridge_something_total counter', '# TYPE homebridge_something_total counter',
'homebridge_something_total{name="counter"} 100 1577836800000', 'homebridge_something_total{name="counter"} 100 1577836800000',
'',
].join('\n'), ].join('\n'),
) )
}) })
@ -122,7 +121,6 @@ describe('Fastify HTTP adapter', () => {
'', '',
'# TYPE homebridge_something_total counter', '# TYPE homebridge_something_total counter',
'homebridge_something_total{name="counter"} 100 1577836800000', 'homebridge_something_total{name="counter"} 100 1577836800000',
'',
].join('\n'), ].join('\n'),
) )
}) })

View file

@ -8,16 +8,14 @@ describe('Render prometheus metrics', () => {
test('Renders simple metric', () => { test('Renders simple metric', () => {
expect(renderer.render([new Metric('metric', 0.000001)])).toEqual( expect(renderer.render([new Metric('metric', 0.000001)])).toEqual(
`# TYPE prefix_metric gauge `# TYPE prefix_metric gauge
prefix_metric 0.000001 prefix_metric 0.000001`,
`,
) )
}) })
test('Renders simple metric with timestamp', () => { test('Renders simple metric with timestamp', () => {
expect(renderer.render([new Metric('metric', 0.000001, new Date('2000-01-01 00:00:00 UTC'))])).toEqual( expect(renderer.render([new Metric('metric', 0.000001, new Date('2000-01-01 00:00:00 UTC'))])).toEqual(
`# TYPE prefix_metric gauge `# TYPE prefix_metric gauge
prefix_metric 0.000001 946684800000 prefix_metric 0.000001 946684800000`,
`,
) )
}) })
@ -28,8 +26,7 @@ prefix_metric 0.000001 946684800000
]), ]),
).toEqual( ).toEqual(
`# TYPE prefix_metric gauge `# TYPE prefix_metric gauge
prefix_metric{label="Some Label"} 0.000001 946684800000 prefix_metric{label="Some Label"} 0.000001 946684800000`,
`,
) )
}) })
@ -41,8 +38,7 @@ prefix_metric{label="Some Label"} 0.000001 946684800000
]), ]),
).toEqual( ).toEqual(
`# TYPE prefix_some_metric_total counter `# TYPE prefix_some_metric_total counter
prefix_some_metric_total{label="Some Label"} 42 946684800000 prefix_some_metric_total{label="Some Label"} 42 946684800000`,
`,
) )
} }
}) })
@ -60,40 +56,35 @@ prefix_some_gauge 10 946684800000
prefix_some_gauge 20 946684800000 prefix_some_gauge 20 946684800000
# TYPE prefix_another_gauge gauge # TYPE prefix_another_gauge gauge
prefix_another_gauge 30 946684800000 prefix_another_gauge 30 946684800000`,
`,
) )
}) })
test('Sanitizes metric names', () => { test('Sanitizes metric names', () => {
expect(renderer.render([new Metric('mätric name', 0)])).toEqual( expect(renderer.render([new Metric('mätric name', 0)])).toEqual(
`# TYPE prefix_m_tric_name gauge `# TYPE prefix_m_tric_name gauge
prefix_m_tric_name 0 prefix_m_tric_name 0`,
`,
) )
}) })
test('Sanitizes label names', () => { test('Sanitizes label names', () => {
expect(renderer.render([new Metric('metric', 0, null, { 'yet another label': 'foo' })])).toEqual( expect(renderer.render([new Metric('metric', 0, null, { 'yet another label': 'foo' })])).toEqual(
`# TYPE prefix_metric gauge `# TYPE prefix_metric gauge
prefix_metric{yet_another_label="foo"} 0 prefix_metric{yet_another_label="foo"} 0`,
`,
) )
}) })
test('Escapes newlines in attribute value', () => { test('Escapes newlines in attribute value', () => {
expect(renderer.render([new Metric('metric', 0, null, { label: 'foo\nbar' })])).toEqual( expect(renderer.render([new Metric('metric', 0, null, { label: 'foo\nbar' })])).toEqual(
`# TYPE prefix_metric gauge `# TYPE prefix_metric gauge
prefix_metric{label="foo\\nbar"} 0 prefix_metric{label="foo\\nbar"} 0`,
`,
) )
}) })
test('Escapes quotes in attribute value', () => { test('Escapes quotes in attribute value', () => {
expect(renderer.render([new Metric('metric', 0, null, { label: 'foo"bar' })])).toEqual( expect(renderer.render([new Metric('metric', 0, null, { label: 'foo"bar' })])).toEqual(
`# TYPE prefix_metric gauge `# TYPE prefix_metric gauge
prefix_metric{label="foo\\"bar"} 0 prefix_metric{label="foo\\"bar"} 0`,
`,
) )
}) })
}) })

View file

@ -10,7 +10,7 @@
"rootDir": "./", "rootDir": "./",
"strict": true, "strict": true,
"esModuleInterop": true, "esModuleInterop": true,
"verbatimModuleSyntax": false, "importsNotUsedAsValues": "error",
"noImplicitAny": true, "noImplicitAny": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"tsBuildInfoFile": ".tsbuildinfo", "tsBuildInfoFile": ".tsbuildinfo",