Skip to content
Open
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
6 changes: 6 additions & 0 deletions apps/server-nestjs/src/modules/healthz/healthz.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { DatabaseHealthService } from '../../cpin-module/infrastructure/database
import { ArgoCDHealthService } from '../argocd/argocd-health.service'
import { GitlabHealthService } from '../gitlab/gitlab-health.service'
import { KeycloakHealthService } from '../keycloak/keycloak-health.service'
import { NexusHealthService } from '../nexus/nexus-health.service'
import { RegistryHealthService } from '../registry/registry-health.service'
import { VaultHealthService } from '../vault/vault-health.service'

@Controller('api/v1/healthz')
Expand All @@ -14,6 +16,8 @@ export class HealthzController {
@Inject(KeycloakHealthService) private readonly keycloak: KeycloakHealthService,
@Inject(GitlabHealthService) private readonly gitlab: GitlabHealthService,
@Inject(VaultHealthService) private readonly vault: VaultHealthService,
@Inject(NexusHealthService) private readonly nexus: NexusHealthService,
@Inject(RegistryHealthService) private readonly registry: RegistryHealthService,
@Inject(ArgoCDHealthService) private readonly argocd: ArgoCDHealthService,
) {}

Expand All @@ -25,6 +29,8 @@ export class HealthzController {
() => this.keycloak.check('keycloak'),
() => this.gitlab.check('gitlab'),
() => this.vault.check('vault'),
() => this.nexus.check('nexus'),
() => this.registry.check('registry'),
() => this.argocd.check('argocd'),
])
}
Expand Down
4 changes: 4 additions & 0 deletions apps/server-nestjs/src/modules/healthz/healthz.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { DatabaseModule } from '../../cpin-module/infrastructure/database/databa
import { ArgoCDModule } from '../argocd/argocd.module'
import { GitlabModule } from '../gitlab/gitlab.module'
import { KeycloakModule } from '../keycloak/keycloak.module'
import { NexusModule } from '../nexus/nexus.module'
import { RegistryModule } from '../registry/registry.module'
import { VaultModule } from '../vault/vault.module'
import { HealthzController } from './healthz.controller'

Expand All @@ -14,6 +16,8 @@ import { HealthzController } from './healthz.controller'
KeycloakModule,
GitlabModule,
VaultModule,
NexusModule,
RegistryModule,
ArgoCDModule,
],
controllers: [HealthzController],
Expand Down
35 changes: 35 additions & 0 deletions apps/server-nestjs/src/modules/nexus/nexus-client.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { TestingModule } from '@nestjs/testing'
import { Test } from '@nestjs/testing'
import { beforeEach, describe, expect, it } from 'vitest'
import { ConfigurationService } from '../../cpin-module/infrastructure/configuration/configuration.service'
import { NexusClientService } from './nexus-client.service'
import { NexusHttpClientService } from './nexus-http-client.service'

function createNexusServiceTestingModule() {
return Test.createTestingModule({
providers: [
NexusClientService,
NexusHttpClientService,
{
provide: ConfigurationService,
useValue: {
nexusSecretExposedUrl: 'https://nexus.example',
projectRootDir: 'forge',
} satisfies Partial<ConfigurationService>,
},
],
})
}

describe('nexusClientService', () => {
let service: NexusClientService

beforeEach(async () => {
const module: TestingModule = await createNexusServiceTestingModule().compile()
service = module.get(NexusClientService)
})

it('should be defined', () => {
expect(service).toBeDefined()
})
})
299 changes: 299 additions & 0 deletions apps/server-nestjs/src/modules/nexus/nexus-client.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
import type { NexusFetchOptions, NexusResponse } from './nexus-http-client.service'
import { Inject, Injectable } from '@nestjs/common'
import { ConfigurationService } from '../../cpin-module/infrastructure/configuration/configuration.service'
import { StartActiveSpan } from '../../cpin-module/infrastructure/telemetry/telemetry.decorator'
import { NexusError, NexusHttpClientService } from './nexus-http-client.service'
import { generateMavenHostedRepoName, generateNpmHostedRepoName } from './nexus.utils'

interface NexusRepositoryStorage {
blobStoreName: string
strictContentTypeValidation: boolean
writePolicy?: string
}

interface NexusRepositoryCleanup {
policyNames: string[]
}

interface NexusRepositoryComponent {
proprietaryComponents: boolean
}

interface NexusRepositoryGroup {
memberNames: string[]
}

interface NexusMavenHostedRepositoryUpsertRequest {
name: string
online: boolean
storage: NexusRepositoryStorage & { writePolicy: string }
cleanup: NexusRepositoryCleanup
component: NexusRepositoryComponent
maven: {
versionPolicy: string
layoutPolicy: string
contentDisposition: string
}
}

interface NexusMavenGroupRepositoryUpsertRequest {
name: string
online: boolean
storage: Omit<NexusRepositoryStorage, 'writePolicy'>
group: NexusRepositoryGroup
}

interface NexusNpmHostedRepositoryUpsertRequest {
name: string
online: boolean
storage: NexusRepositoryStorage & { writePolicy: string }
cleanup: NexusRepositoryCleanup
component: NexusRepositoryComponent
}

interface NexusNpmGroupRepositoryUpsertRequest {
name: string
online: boolean
storage: Omit<NexusRepositoryStorage, 'writePolicy'>
group: NexusRepositoryGroup
}

interface NexusRepositoryViewPrivilegeUpsertRequest {
name: string
description: string
actions: string[]
format: string
repository: string
}

interface NexusRoleCreateRequest {
id: string
name: string
description: string
privileges: string[]
}

interface NexusRoleUpdateRequest {
id: string
name: string
privileges: string[]
}

interface NexusUserCreateRequest {
userId: string
firstName: string
lastName: string
emailAddress: string
password: string
status: string
roles: string[]
}

@Injectable()
export class NexusClientService {
constructor(
@Inject(ConfigurationService) private readonly config: ConfigurationService,
@Inject(NexusHttpClientService) private readonly http: NexusHttpClientService,
) {}

getProjectSecrets(args: { projectSlug: string, enableMaven: boolean, enableNpm: boolean }) {
const projectSlug = args.projectSlug
const nexusUrl = this.config.nexusSecretExposedUrl!
const secrets: Record<string, string> = {}
if (args.enableMaven) {
secrets.MAVEN_REPO_RELEASE = `${nexusUrl}/${generateMavenHostedRepoName(projectSlug, 'release')}`
secrets.MAVEN_REPO_SNAPSHOT = `${nexusUrl}/${generateMavenHostedRepoName(projectSlug, 'snapshot')}`
}
if (args.enableNpm) {
secrets.NPM_REPO = `${nexusUrl}/${generateNpmHostedRepoName(projectSlug)}`
}
return secrets
}

@StartActiveSpan()
private async fetch<T = unknown>(path: string, options: NexusFetchOptions = {}): Promise<NexusResponse<T>> {
return this.http.fetch(path, options)
}

@StartActiveSpan()
async getRepositoriesMavenHosted(name: string): Promise<any | null> {

Check notice on line 119 in apps/server-nestjs/src/modules/nexus/nexus-client.service.ts

View check run for this annotation

cloud-pi-native-sonarqube / SonarQube Code Analysis

apps/server-nestjs/src/modules/nexus/nexus-client.service.ts#L119

'any' overrides all other types in this union type.
try {
const res = await this.fetch(`/repositories/maven/hosted/${name}`, { method: 'GET' })
return res.data
} catch (error) {
if (error instanceof NexusError && error.status === 404) return null
throw error
}
}

@StartActiveSpan()
async createRepositoriesMavenHosted(body: NexusMavenHostedRepositoryUpsertRequest): Promise<void> {
await this.fetch('/repositories/maven/hosted', { method: 'POST', body })
}

@StartActiveSpan()
async updateRepositoriesMavenHosted(name: string, body: NexusMavenHostedRepositoryUpsertRequest): Promise<void> {
await this.fetch(`/repositories/maven/hosted/${name}`, { method: 'PUT', body })
}

@StartActiveSpan()
async createRepositoriesMavenGroup(body: NexusMavenGroupRepositoryUpsertRequest): Promise<void> {
await this.fetch('/repositories/maven/group', { method: 'POST', body })
}

@StartActiveSpan()
async getRepositoriesMavenGroup(name: string): Promise<any | null> {

Check notice on line 145 in apps/server-nestjs/src/modules/nexus/nexus-client.service.ts

View check run for this annotation

cloud-pi-native-sonarqube / SonarQube Code Analysis

apps/server-nestjs/src/modules/nexus/nexus-client.service.ts#L145

'any' overrides all other types in this union type.
try {
const res = await this.fetch(`/repositories/maven/group/${name}`, { method: 'GET' })
return res.data
} catch (error) {
if (error instanceof NexusError && error.status === 404) return null
throw error
}
}

@StartActiveSpan()
async getRepositoriesNpmHosted(name: string): Promise<any | null> {

Check notice on line 156 in apps/server-nestjs/src/modules/nexus/nexus-client.service.ts

View check run for this annotation

cloud-pi-native-sonarqube / SonarQube Code Analysis

apps/server-nestjs/src/modules/nexus/nexus-client.service.ts#L156

'any' overrides all other types in this union type.
try {
const res = await this.fetch(`/repositories/npm/hosted/${name}`, { method: 'GET' })
return res.data
} catch (error) {
if (error instanceof NexusError && error.status === 404) return null
throw error
}
}

@StartActiveSpan()
async createRepositoriesNpmHosted(body: NexusNpmHostedRepositoryUpsertRequest): Promise<void> {
await this.fetch('/repositories/npm/hosted', { method: 'POST', body })
}

@StartActiveSpan()
async updateRepositoriesNpmHosted(name: string, body: NexusNpmHostedRepositoryUpsertRequest): Promise<void> {
await this.fetch(`/repositories/npm/hosted/${name}`, { method: 'PUT', body })
}

@StartActiveSpan()
async getRepositoriesNpmGroup(name: string): Promise<any | null> {

Check notice on line 177 in apps/server-nestjs/src/modules/nexus/nexus-client.service.ts

View check run for this annotation

cloud-pi-native-sonarqube / SonarQube Code Analysis

apps/server-nestjs/src/modules/nexus/nexus-client.service.ts#L177

'any' overrides all other types in this union type.
try {
const res = await this.fetch(`/repositories/npm/group/${name}`, { method: 'GET' })
return res.data
} catch (error) {
if (error instanceof NexusError && error.status === 404) return null
throw error
}
}

@StartActiveSpan()
async postRepositoriesNpmGroup(body: NexusNpmGroupRepositoryUpsertRequest): Promise<void> {
await this.fetch('/repositories/npm/group', { method: 'POST', body })
}

@StartActiveSpan()
async putRepositoriesNpmGroup(name: string, body: NexusNpmGroupRepositoryUpsertRequest): Promise<void> {
await this.fetch(`/repositories/npm/group/${name}`, { method: 'PUT', body })
}

@StartActiveSpan()
async getSecurityPrivileges(name: string): Promise<any | null> {

Check notice on line 198 in apps/server-nestjs/src/modules/nexus/nexus-client.service.ts

View check run for this annotation

cloud-pi-native-sonarqube / SonarQube Code Analysis

apps/server-nestjs/src/modules/nexus/nexus-client.service.ts#L198

'any' overrides all other types in this union type.
try {
const res = await this.fetch(`/security/privileges/${name}`, { method: 'GET' })
return res.data
} catch (error) {
if (error instanceof NexusError && error.status === 404) return null
throw error
}
}

@StartActiveSpan()
async createSecurityPrivilegesRepositoryView(body: NexusRepositoryViewPrivilegeUpsertRequest): Promise<void> {
await this.fetch('/security/privileges/repository-view', { method: 'POST', body })
}

@StartActiveSpan()
async updateSecurityPrivilegesRepositoryView(name: string, body: NexusRepositoryViewPrivilegeUpsertRequest): Promise<void> {
await this.fetch(`/security/privileges/repository-view/${name}`, { method: 'PUT', body })
}

@StartActiveSpan()
async deleteSecurityPrivileges(name: string): Promise<void> {
try {
await this.fetch(`/security/privileges/${name}`, { method: 'DELETE' })
} catch (error) {
if (error instanceof NexusError && error.status === 404) return
throw error
}
}

@StartActiveSpan()
async getSecurityRoles(id: string): Promise<any | null> {

Check notice on line 229 in apps/server-nestjs/src/modules/nexus/nexus-client.service.ts

View check run for this annotation

cloud-pi-native-sonarqube / SonarQube Code Analysis

apps/server-nestjs/src/modules/nexus/nexus-client.service.ts#L229

'any' overrides all other types in this union type.
try {
const res = await this.fetch(`/security/roles/${id}`, { method: 'GET' })
return res.data
} catch (error) {
if (error instanceof NexusError && error.status === 404) return null
throw error
}
}

@StartActiveSpan()
async createSecurityRoles(body: NexusRoleCreateRequest): Promise<void> {
await this.fetch('/security/roles', { method: 'POST', body })
}

@StartActiveSpan()
async updateSecurityRoles(id: string, body: NexusRoleUpdateRequest): Promise<void> {
await this.fetch(`/security/roles/${id}`, { method: 'PUT', body })
}

@StartActiveSpan()
async deleteSecurityRoles(id: string): Promise<void> {
try {
await this.fetch(`/security/roles/${id}`, { method: 'DELETE' })
} catch (error) {
if (error instanceof NexusError && error.status === 404) return
throw error
}
}

@StartActiveSpan()
async getSecurityUsers(userId: string): Promise<{ userId: string }[]> {
const query = new URLSearchParams({ userId }).toString()
const res = await this.fetch<{ userId: string }[]>(`/security/users?${query}`, { method: 'GET' })
return (res.data as any) ?? []
}

@StartActiveSpan()
async updateSecurityUsersChangePassword(userId: string, password: string): Promise<void> {
await this.fetch(`/security/users/${userId}/change-password`, {
method: 'PUT',
body: password,
headers: { 'Content-Type': 'text/plain' },
})
}

@StartActiveSpan()
async createSecurityUsers(body: NexusUserCreateRequest): Promise<void> {
await this.fetch('/security/users', { method: 'POST', body })
}

@StartActiveSpan()
async deleteSecurityUsers(userId: string): Promise<void> {
try {
await this.fetch(`/security/users/${userId}`, { method: 'DELETE' })
} catch (error) {
if (error instanceof NexusError && error.status === 404) return
throw error
}
}

@StartActiveSpan()
async deleteRepositoriesByName(name: string): Promise<void> {
try {
await this.fetch(`/repositories/${name}`, { method: 'DELETE' })
} catch (error) {
if (error instanceof NexusError && error.status === 404) return
throw error
}
}
}
Loading
Loading