Skip to content

Health module and health endpoints#4

Closed
saadmoumou wants to merge 28 commits intomasterfrom
healthModule-and-health-endpoints
Closed

Health module and health endpoints#4
saadmoumou wants to merge 28 commits intomasterfrom
healthModule-and-health-endpoints

Conversation

@saadmoumou
Copy link
Copy Markdown
Contributor

Summary

  • What does this PR change?

Why

  • Why is this change needed?

Checklist

  • Added/updated tests (if behavior changed)
  • npm run lint passes
  • npm run typecheck passes
  • npm test passes
  • npm run build passes
  • Added a changeset (npx changeset) if this affects consumers

Notes

  • Anything reviewers should pay attention to?

Zaiidmo and others added 28 commits March 29, 2026 12:47
…ckages

- Replace git tag --list strategy with package.json-driven tag validation
  in all 16 publish workflows; use git rev-parse to verify the exact tag
  exists rather than guessing the latest repo-wide tag
- Update error guidance to reflect feat/** → develop → master flow
- Standardize dependabot to npm-only, grouped, monthly cadence across
  all 16 packages; remove github-actions ecosystem updates
- Add missing dependabot.yml to AuthKit-UI, ChartKit-UI, HealthKit,
  HooksKit, paymentkit, StorageKit
* chore(config): add @interfaces/* and @indicators/* path aliases

* chore(config): rename eslint.config.js to .mjs to fix ESM loading

* chore(git): fix husky pre-commit hook for v10 compatibility

* feat(interfaces): add IHealthIndicator, HealthStatus, HealthIndicatorResult

* feat(indicators): add PostgresHealthIndicator (SELECT 1 + timeout)

* test(indicators): add PostgresHealthIndicator unit tests (success/error/timeout)

* feat(indicators): add RedisHealthIndicator (PING + timeout)

* test(indicators): add RedisHealthIndicator unit tests (success/error/timeout)

* feat(indicators): add HttpHealthIndicator (GET + 2xx check + timeout)

* test(indicators): add HttpHealthIndicator unit tests (2xx/non-2xx/network/timeout)

* chore(deps): update package-lock after npm install

---------

Co-authored-by: saad moumou <saad.moumou.coder@gmail.com>
@saadmoumou saadmoumou requested review from a team and Copilot April 2, 2026 11:28
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 2, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a reusable @ciscode/health-kit NestJS module providing liveness/readiness HTTP endpoints backed by a pluggable indicator system, plus project/tooling updates to support publishing and CI.

Changes:

  • Introduces HealthKitModule, HealthService, and /live + /ready endpoints (controller factory).
  • Adds built-in health indicators (Postgres, Redis, Mongo, HTTP) with Jest unit tests.
  • Updates TS path aliases, ESLint/lint-staged/Husky, and GitHub workflows for CI + publishing.

Reviewed changes

Copilot reviewed 26 out of 29 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
tsconfig.json Adds path aliases for @interfaces/* and @indicators/*.
tsconfig.build.json Forces build output to CommonJS/Node resolution for dist/.
src/services/health.service.ts Implements concurrent liveness/readiness checks via Promise.allSettled.
src/services/health.service.spec.ts Unit tests for HealthService success/failure/concurrency behavior.
src/interfaces/health-indicator.interface.ts Defines indicator contract and result types.
src/indicators/redis.indicator.ts Redis PING-based indicator with timeout handling.
src/indicators/redis.indicator.spec.ts Tests Redis indicator up/down/timeout paths.
src/indicators/postgres.indicator.ts Postgres SELECT 1 indicator with timeout handling.
src/indicators/postgres.indicator.spec.ts Tests Postgres indicator up/down/timeout paths.
src/indicators/mongo.indicator.ts Mongo { ping: 1 } indicator with timeout handling.
src/indicators/mongo.indicator.spec.ts Tests Mongo indicator up/down/timeout paths.
src/indicators/http.indicator.ts HTTP GET-based indicator using fetch + AbortController timeout.
src/indicators/http.indicator.spec.ts Tests HTTP indicator for 2xx, non-2xx, errors, and timeout.
src/index.ts Switches public exports from template to health-kit API exports.
src/health-kit.module.ts Adds module registration API wiring indicators into HealthService + controller.
src/controllers/health.controller.ts Adds controller factory for /live and /ready endpoints.
src/controllers/health.controller.spec.ts Tests controller behavior (200 vs 503 via exception).
package.json Renames package, updates description/repo, adjusts peer deps, and scripts.
package-lock.json Updates lockfile metadata and dev dependency set.
lint-staged.config.mjs Adds lint-staged config for Prettier + ESLint autofix.
jest.config.ts Adds Jest mappers for new TS path aliases.
eslint.config.mjs Reworks ESLint flat config (TS + import ordering + ignores).
eslint.config.js Removes old ESLint config file.
.husky/pre-commit Changes pre-commit hook to run lint-staged.
.github/workflows/release-check.yml Makes Sonar scan run in PR CI and adds commit status reporting.
.github/workflows/publish.yml Publishes on master push with version-tag validation and provenance.
.github/workflows/pr-validation.yml Updates CI Node version to 22.
.github/dependabot.yml Adds monthly Dependabot updates for npm deps.
.github/CODEOWNERS Adds CODEOWNERS ownership rules.

Comment on lines +23 to +31
* import { HealthModule } from '@ciscode/health-kit';
* import { MongoHealthIndicator } from '@ciscode/health-kit';
*
* @Module({
* imports: [
* HealthModule.register({
* path: 'health',
* liveness: [],
* readiness: [new MongoHealthIndicator(dataSource)],
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example uses HealthModule.register(...), but the module exported by this package is HealthKitModule. Also, the example passes dataSource into MongoHealthIndicator, but that indicator expects a Mongo Db-like handle (e.g. mongoose.connection.db). Update the example to match the actual public API and expected constructor argument.

Suggested change
* import { HealthModule } from '@ciscode/health-kit';
* import { MongoHealthIndicator } from '@ciscode/health-kit';
*
* @Module({
* imports: [
* HealthModule.register({
* path: 'health',
* liveness: [],
* readiness: [new MongoHealthIndicator(dataSource)],
* import { HealthKitModule, MongoHealthIndicator } from '@ciscode/health-kit';
* import mongoose from 'mongoose';
*
* @Module({
* imports: [
* HealthKitModule.register({
* path: 'health',
* liveness: [],
* readiness: [new MongoHealthIndicator(mongoose.connection.db)],

Copilot uses AI. Check for mistakes.
Comment on lines +27 to +30
async live(): Promise<HealthCheckResult> {
const result = await this.healthService.checkLiveness();
if (result.status === "error") throw new ServiceUnavailableException(result);
return result;
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throwing ServiceUnavailableException(result) will serialize and return the full indicator payload (including internal dependency names and error messages) to callers. Consider returning a minimal 503 response (or making detailed output configurable) to avoid leaking operational details outside trusted networks.

Copilot uses AI. Check for mistakes.
Comment on lines +70 to +77
- name: Build
run: npm run build --if-present

- name: Run tests (if present)
run: npm test --if-present
continue-on-error: false
- name: Lint
run: npm run lint --if-present 2>/dev/null || true

- name: Build package
run: npm run build
- name: Test
run: npm test --if-present 2>/dev/null || true
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These lint/test steps swallow failures (|| true), so the workflow can publish even when linting or tests fail. Remove the error suppression so publishing is blocked on failing checks (or make it explicitly conditional with a separate opt-in).

Copilot uses AI. Check for mistakes.
Comment on lines +55 to +57

echo "✅ package.json version: $PKG_VERSION"
echo "✅ Tag $TAG exists in repo"
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tag validation only checks that the version tag exists somewhere in the repo; it does not verify that the current commit being published is actually tagged with that version. Consider asserting the tag points at HEAD (or at least that package.json version matches the commit tag) to prevent publishing an unintended revision.

Suggested change
echo "✅ package.json version: $PKG_VERSION"
echo "✅ Tag $TAG exists in repo"
HEAD_SHA=$(git rev-parse HEAD)
TAG_SHA=$(git rev-parse "$TAG")
if [[ "$HEAD_SHA" != "$TAG_SHA" ]]; then
echo "❌ ERROR: Tag $TAG does not point at HEAD."
echo ""
echo "HEAD commit: $HEAD_SHA"
echo "Tag commit: $TAG_SHA"
echo ""
echo "Ensure that the commit being pushed to master is the one tagged with $TAG."
echo "Typically you should:"
echo " 1. Create the version with 'npm version patch|minor|major' on the release commit"
echo " 2. Push branch + tag so that $TAG points at the commit being published"
exit 1
fi
echo "✅ package.json version: $PKG_VERSION"
echo "✅ Tag $TAG exists in repo"
echo "✅ Tag $TAG points at HEAD"

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +60
async check(): Promise<HealthIndicatorResult> {
try {
await Promise.race([this.client.ping(), this._timeout()]);
return { name: "redis", status: "up" };
} catch (error) {
return {
name: "redis",
status: "down",
message: error instanceof Error ? error.message : "Unknown error",
};
}
}

private _timeout(): Promise<never> {
return new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), this.timeoutMs),
);
}
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The timeout implementation starts a setTimeout but never clears it on success, so each health check schedules a timer that will still fire later. Consider storing the timer handle and clearTimeout it in a finally, or use an AbortSignal/timeout helper that can be cancelled.

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +34
* @example
* ```typescript
* import Redis from 'ioredis';
*
* const redis = new Redis({ host: 'localhost', port: 6379 });
*
* HealthModule.register({
* path: 'health',
* liveness: [],
* readiness: [new RedisHealthIndicator(redis)],
* });
* ```
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example references HealthModule.register(...), but the exported module is HealthKitModule (per src/index.ts). Update the docs so consumers can copy/paste a working example.

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +33
* @example
* ```typescript
* // With a pg.Pool
* const pool = new Pool({ connectionString: process.env.DATABASE_URL });
*
* HealthModule.register({
* path: 'health',
* liveness: [],
* readiness: [new PostgresHealthIndicator(pool)],
* });
* ```
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example references HealthModule.register(...), but the exported module is HealthKitModule (per src/index.ts). Update the docs so consumers can copy/paste a working example.

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +43
* @example
* ```typescript
* import mongoose from 'mongoose';
*
* HealthModule.register({
* path: 'health',
* liveness: [],
* readiness: [new MongoHealthIndicator(mongoose.connection.db)],
* });
* ```
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example references HealthModule.register(...), but the exported module is HealthKitModule (per src/index.ts). Update the docs so consumers can copy/paste a working example.

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +26
* @example
* ```typescript
* HealthModule.register({
* path: 'health',
* liveness: [],
* readiness: [
* new HttpHealthIndicator('https://api.example.com/health'),
* ],
* });
* ```
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example references HealthModule.register(...), but the exported module is HealthKitModule (per src/index.ts). Update the docs so consumers can copy/paste a working example.

Copilot uses AI. Check for mistakes.
-Dsonar.organization=${{ env.SONAR_ORGANIZATION }}
-Dsonar.projectKey=${{ env.SONAR_PROJECT_KEY }}
-Dsonar.sources=src
-Dsonar.tests=test
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sonar is configured with -Dsonar.tests=test, but this repo’s Jest config also places many tests under src/**/*.spec.ts. As-is, Sonar may treat src/**/*.spec.ts as source code (or miss tests/coverage attribution). Consider setting sonar.tests to include src (or adding explicit sonar.test.inclusions / sonar.exclusions for **/*.spec.ts).

Suggested change
-Dsonar.tests=test
-Dsonar.tests=src,test
-Dsonar.test.inclusions=**/*.spec.ts

Copilot uses AI. Check for mistakes.
@saadmoumou saadmoumou deleted the healthModule-and-health-endpoints branch April 2, 2026 11:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants