Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/server-nestjs/src/modules/healthz/healthz.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ConfigurationService } from '../infrastructure/configuration/configurat
import { DatabaseHealthService } from '../infrastructure/database/database-health.service'
import { KeycloakHealthService } from '../keycloak/keycloak-health.service'
import { NexusHealthService } from '../nexus/nexus-health.service'
import { OpenCdsHealthService } from '../opencds/opencds-health.service'
import { ServiceChainHealthService } from '../service-chain/service-chain-health.service'
import { RegistryHealthService } from '../registry/registry-health.service'
import { VaultHealthService } from '../vault/vault-health.service'

Expand All @@ -21,7 +21,7 @@ export class HealthzController {
@Inject(NexusHealthService) private readonly nexus: NexusHealthService,
@Inject(RegistryHealthService) private readonly registry: RegistryHealthService,
@Inject(ArgoCDHealthService) private readonly argocd: ArgoCDHealthService,
@Inject(OpenCdsHealthService) private readonly opencds: OpenCdsHealthService,
@Inject(ServiceChainHealthService) private readonly opencds: ServiceChainHealthService,
@Inject(ConfigurationService) private readonly config: ConfigurationService,
) {}

Expand Down
4 changes: 2 additions & 2 deletions apps/server-nestjs/src/modules/healthz/healthz.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ConfigurationModule } from '../infrastructure/configuration/configurati
import { DatabaseModule } from '../infrastructure/database/database.module'
import { KeycloakModule } from '../keycloak/keycloak.module'
import { NexusModule } from '../nexus/nexus.module'
import { OpenCdsModule } from '../opencds/opencds.module'
import { ServiceChainModule } from '../service-chain/service-chain.module'
import { RegistryModule } from '../registry/registry.module'
import { VaultModule } from '../vault/vault.module'
import { HealthzController } from './healthz.controller'
Expand All @@ -22,7 +22,7 @@ import { HealthzController } from './healthz.controller'
RegistryModule,
ArgoCDModule,
ConfigurationModule,
OpenCdsModule,
ServiceChainModule,
],
controllers: [HealthzController],
})
Expand Down
11 changes: 0 additions & 11 deletions apps/server-nestjs/src/modules/opencds/opencds.module.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
import { HttpStatus, Inject, Injectable } from '@nestjs/common'
import { HealthIndicatorService } from '@nestjs/terminus'
import { ConfigurationService } from '../infrastructure/configuration/configuration.service'

@Injectable()
export class OpenCdsHealthService {
constructor(
@Inject(ConfigurationService) private readonly config: ConfigurationService,
@Inject(HealthIndicatorService) private readonly healthIndicator: HealthIndicatorService,
) {}

async check(key: string) {
const indicator = this.healthIndicator.check(key)
if (!this.config.openCdsUrl) return indicator.down('Not configured')

try {
const url = new URL('/api/v1/health', this.config.openCdsUrl).toString()
const headers: Record<string, string> = {}
if (this.config.openCdsApiToken) {
headers.Authorization = `Bearer ${this.config.openCdsApiToken}`
}

const response = await fetch(url, { headers })
if (response.status < HttpStatus.INTERNAL_SERVER_ERROR) return indicator.up({ httpStatus: response.status })
return indicator.down({ httpStatus: response.status })
} catch (error) {
return indicator.down(error instanceof Error ? error.message : String(error))
}
}
}
import { HttpStatus, Inject, Injectable } from '@nestjs/common'
import { HealthIndicatorService } from '@nestjs/terminus'
import { ConfigurationService } from '../infrastructure/configuration/configuration.service'
@Injectable()
export class ServiceChainHealthService {
constructor(
@Inject(ConfigurationService) private readonly config: ConfigurationService,
@Inject(HealthIndicatorService) private readonly healthIndicator: HealthIndicatorService,
) {}
async check(key: string) {
const indicator = this.healthIndicator.check(key)
if (!this.config.openCdsUrl) return indicator.down('Not configured')
try {
const url = new URL('/api/v1/health', this.config.openCdsUrl).toString()
const headers: Record<string, string> = {}
if (this.config.openCdsApiToken) {
headers.Authorization = `Bearer ${this.config.openCdsApiToken}`
}
const response = await fetch(url, { headers })
if (response.status < HttpStatus.INTERNAL_SERVER_ERROR) return indicator.up({ httpStatus: response.status })
return indicator.down({ httpStatus: response.status })
} catch (error) {
return indicator.down(error instanceof Error ? error.message : String(error))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { DatabaseModule } from '../infrastructure/database/database.module'
import { EventsModule } from '../infrastructure/events/events.module'
import { UserPermissionModule } from '../infrastructure/permission/user/user.module'
import { OpenCdsClientService } from './open-cds-client.service'
import { ServiceChainHealthService } from './service-chain-health.service'
import { ServiceChainController } from './service-chain.controller'
import { ServiceChainService } from './service-chain.service'

Expand All @@ -17,7 +18,7 @@ import { ServiceChainService } from './service-chain.service'
UserPermissionModule,
],
controllers: [ServiceChainController],
providers: [OpenCdsClientService, ServiceChainService],
exports: [ServiceChainService],
providers: [OpenCdsClientService, ServiceChainHealthService, ServiceChainService],
exports: [ServiceChainService, ServiceChainHealthService],
})
export class ServiceChainModule {}
8 changes: 7 additions & 1 deletion packages/eslintconfig/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@
"license": "ISC",
"main": "src/index.js",
"types": "types/index.d.ts",
"scripts": {
"test": "vitest run",
"format": "eslint src --fix"
},
"devDependencies": {
"@antfu/eslint-config": "catalog:tools",
"eslint": "catalog:tools"
"eslint": "catalog:tools",
"eslint-plugin-format": "catalog:tools",
"vitest": "catalog:test"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
Expand Down
6 changes: 6 additions & 0 deletions packages/eslintconfig/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ export default antfu(
'yaml/indent': ['error', 2, { indentBlockSequences: true, indicatorValueIndent: 2 }],
},
},
formatters: {
markdown: true,
json: true,
// jsonc is already covered by the json formatter (prettier keeps comments).
// html: true, // only if apps/client/index.html should be formatted (single file today)
},
ignores: [
'**/node_modules',
'**/prisma/migrations',
Expand Down
101 changes: 101 additions & 0 deletions packages/eslintconfig/src/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { ESLint } from 'eslint'
import { describe, expect, it } from 'vitest'

const __dirname = dirname(fileURLToPath(import.meta.url))
const configFile = join(__dirname, 'index.js')

// ponytail: spin up one ESLint instance per test for isolation; cheap enough at this scale.
async function formatText(input: string, filePath: string): Promise<string> {
const eslint = new ESLint({ overrideConfigFile: configFile, fix: true, cwd: __dirname })
const [result] = await eslint.lintText(input, { filePath })
if (result.fatalErrorCount > 0) {
throw new Error(`fatal lint error: ${JSON.stringify(result.messages)}`)
}
return result.output ?? input
}

describe('eslint-config formatter (markdown + json)', () => {
describe('markdown list normalization', () => {
it('rewrites * bullets to - per CommonMark style', async () => {
const out = await formatText('# Title\n\n* a\n* b\n', 'doc.md')
expect(out).toContain('- a\n- b')
expect(out).not.toContain('* a')
})

it('collapses 3+ blank lines to one', async () => {
const out = await formatText('# T\n\n\n\nbody\n', 'doc.md')
expect(out).toBe('# T\n\nbody\n')
})

it('trims trailing whitespace on each line', async () => {
const out = await formatText('text \nmore\t\n', 'doc.md')
expect(out).toBe('text\nmore\n')
})
})

describe('idempotency (re-run yields identical output)', () => {
it('is stable on markdown', async () => {
const input = '# Title\n\n- a\n- b\n\ntext\n'
const once = await formatText(input, 'doc.md')
const twice = await formatText(once, 'doc.md')
expect(twice).toBe(once)
})

it('is stable on json', async () => {
const input = '{\n "a": 1,\n "b": 2\n}\n'
const once = await formatText(input, 'data.json')
const twice = await formatText(once, 'data.json')
expect(twice).toBe(once)
})
})

describe('json formatting', () => {
// ponytail: eslint-plugin-format's prettier json path normalizes inline spacing
// and ensures a trailing newline but does NOT expand objects across lines
// (verified empirically: wide JSON stays single-line). Assert the real output.
it('normalizes spacing and adds a trailing newline', async () => {
const input = '{"a":1,"b":2}'
const out = await formatText(input, 'data.json')
expect(out.trimEnd()).toBe('{ "a": 1, "b": 2 }')
expect(out.endsWith('\n')).toBe(true)
})

it('does not mangle valid json content', async () => {
const input = '{\n "nested": { "x": [1, 2, 3] }\n}\n'
const out = await formatText(input, 'data.json')
expect(JSON.parse(out)).toEqual({ nested: { x: [1, 2, 3] } })
})
})

describe('extension coverage (jsonc already served by json formatter)', () => {
it('normalizes .jsonc spacing while preserving comments', async () => {
const input = '{\n // keep me\n "a" :1 , "b":2 }\n'
const out = await formatText(input, 'data.jsonc')
// The json formatter's prettier parser keeps comments AND normalizes spacing,
// so .jsonc needs no separate formatter (correcting the parent's misnomer comment).
expect(out).toContain('// keep me')
expect(out).toContain('"a": 1,')
expect(out.trimEnd()).toContain('"a": 1, "b": 2')
})
})

describe('performance (must not add meaningful latency to the dev pipeline)', () => {
it('formats 200 mixed files within a tight budget', async () => {
// ponytail: single shared ESLint instance, real formatter cost only.
const eslint = new ESLint({ overrideConfigFile: configFile, fix: true, cwd: __dirname })
const md = '# T\n\n* a\n* b\n \ntext \n'
const json = '{"alpha":1,"beta":2}'
const files = Array.from({ length: 200 }, (_, i) =>
(i % 2 === 0
? eslint.lintText(md, { filePath: `f${i}.md` })
: eslint.lintText(json, { filePath: `f${i}.json` })))
const start = performance.now()
await Promise.all(files)
const elapsed = performance.now() - start
// 200 small files must finish well under a second (prettier GQL cache + warm JIT).
expect(elapsed).toBeLessThan(1000)
})
})
})
9 changes: 9 additions & 0 deletions packages/eslintconfig/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { configDefaults, defineConfig } from 'vitest/config'

export default defineConfig({
test: {
environment: 'node',
include: ['src/**/*.spec.{ts,js}'],
exclude: [...configDefaults.exclude],
},
})
Loading
Loading