diff --git a/README-v1-draft.md b/README-v1-draft.md new file mode 100644 index 0000000..ffd8e7b --- /dev/null +++ b/README-v1-draft.md @@ -0,0 +1,5 @@ +# TADIS - Tool for Architecture Discovery + +- [tadis-analyzer](sources/tadis-analyzer) - analyzer backend +- [tadis-analyzer-custom-example](sources/tadis-analyzer-custom-example) - example customization of tadis-analyzer +- [tadis-viewer-d3](sources/tadis-viewer-d3) - web-frontend based on D3 diff --git a/sources/acmi-analyzer-custom-example/src/CustomCollector.module.ts b/sources/acmi-analyzer-custom-example/src/CustomCollector.module.ts index 0627588..3c48279 100644 --- a/sources/acmi-analyzer-custom-example/src/CustomCollector.module.ts +++ b/sources/acmi-analyzer-custom-example/src/CustomCollector.module.ts @@ -1,5 +1,9 @@ import { Module } from '@nestjs/common' -import { CollectorService, ConfigModule, CollectorController, SourceCodeAnalysisModule } from 'acmi-analyzer' + +import { + CollectorService, ConfigModule, CollectorController, SourceCodeAnalysisModule, + KubernetesModule, RabbitMqModule, TransformerModule +} from 'acmi-analyzer' import { CustomCollectorService } from './CustomCollector.service' @@ -11,7 +15,10 @@ const customCollectorProvider = { @Module({ imports: [ ConfigModule, - SourceCodeAnalysisModule + KubernetesModule, + RabbitMqModule, + SourceCodeAnalysisModule, + TransformerModule ], controllers: [ CollectorController diff --git a/sources/acmi-analyzer-custom-example/src/CustomCollector.service.ts b/sources/acmi-analyzer-custom-example/src/CustomCollector.service.ts index a614943..8b1c2b2 100644 --- a/sources/acmi-analyzer-custom-example/src/CustomCollector.service.ts +++ b/sources/acmi-analyzer-custom-example/src/CustomCollector.service.ts @@ -1,14 +1,74 @@ import { Injectable } from '@nestjs/common' -import { System, MicroService } from 'acmi-analyzer' + +import { + System, MicroService, Metadata, KubernetesCollectorService, RabbitMqCollectorService, + FeignClientsFromSourceCodeProducer, AnnotationAnalyzer, SourceLocationDecorator, + ExcludedNodesRemover, MicroserviceWithMessageExchangeMerger, CabinetTransformer, ElementMapping, GitStorageService, ConfigService +} from 'acmi-analyzer' @Injectable() export class CustomCollectorService { + constructor( + private readonly kubernetesCollector: KubernetesCollectorService, + private readonly rabbitMqCollector: RabbitMqCollectorService, + private readonly feignClientsFromSourceCodeProducer: FeignClientsFromSourceCodeProducer, + private readonly annotationAnalyzer: AnnotationAnalyzer, + private readonly sourceLocationDecorator: SourceLocationDecorator, + private readonly excludedNodesRemover: ExcludedNodesRemover, + private readonly microserviceWithMessageExchangeMerger: MicroserviceWithMessageExchangeMerger, + private readonly cabinetTransformer: CabinetTransformer, + private readonly gitStorage: GitStorageService, + private readonly configService: ConfigService + ) { } + public async getAllMicroservices(): Promise { - return [] + return this.kubernetesCollector.getAllMicroservices() } public async getSystem(): Promise { - return new System('my') + let system = new System('') + + system = await this.getAllMicroservicesFromSourceFolder() + // system = await this.kubernetesCollector.transform(system) + // system = await this.rabbitMqCollector.transform(system) + + system = await this.feignClientsFromSourceCodeProducer.transform(system) + + const elementMappings: ElementMapping[] = [ + { + elementName: 'sendToExchange', + nodeTypeToCreate: 'MessageExchange', + nodeTypeDirection: 'target', + edgeType: 'AsyncEventFlow' + }, + { + elementName: 'receiveFromExchange', + nodeTypeToCreate: 'MessageExchange', + nodeTypeDirection: 'source', + edgeType: 'AsyncEventFlow' + } + ] + system = await this.annotationAnalyzer.transform(system, 'EventProcessor', elementMappings) + system = await this.sourceLocationDecorator.transform(system) + + system = await this.excludedNodesRemover.transform(system) + system = await this.microserviceWithMessageExchangeMerger.transform(system) + system = await this.cabinetTransformer.transform(system) + + return system + } + + private async getAllMicroservicesFromSourceFolder(): Promise { + const storageStatus = await this.gitStorage.getStorageStatus() + const metadata: Metadata = { + transformer: 'custom git sources to microservices', + context: this.configService.getSourceFolder() + } + const system = new System('source') + storageStatus.forEach(status => { + system.addMicroService(status.name, undefined, metadata) + }) + return system } } diff --git a/sources/acmi-analyzer/README.md b/sources/acmi-analyzer/README.md index b6a488b..c92b0a7 100644 --- a/sources/acmi-analyzer/README.md +++ b/sources/acmi-analyzer/README.md @@ -1,6 +1,6 @@ # acmi-analyzer -The acmi-analyzer is the analysis part of the architecture analyzer for microservice systems (acmi). It can discover the architecture of a microservice system by collecting information about the system from different sources. +The acmi-analyzer can discover the architecture of a microservice system by collecting information about the system from different sources. ## Installation diff --git a/sources/acmi-analyzer/src/collector/Collector.service.test.ts b/sources/acmi-analyzer/src/collector/Collector.service.test.ts index 429a277..3f2e57d 100644 --- a/sources/acmi-analyzer/src/collector/Collector.service.test.ts +++ b/sources/acmi-analyzer/src/collector/Collector.service.test.ts @@ -13,7 +13,6 @@ import { KubernetesCollectorService } from '../kubernetes/collector/KubernetesCo import { RabbitMqModule } from '../rabbitmq/rabbitmq.module' import { RabbitMqCollectorService } from '../rabbitmq/collector/RabbitMqCollector.service' -import { AnnotationAnalyzer } from './java-source/AnnotationAnalyzer.service' import { System } from 'src/model/ms' import { SourceLocationDecorator } from '../source-code-analysis/git/SourceLocationDecorator.service' @@ -46,10 +45,6 @@ describe(CollectorService.name, () => { const spyOnFeignProducer = jest.spyOn(feignProducer, 'transform') spyOnFeignProducer.mockImplementation(async(system) => system) - const annotationAnalyzer = app.get(AnnotationAnalyzer) - const spyOnAnnotationAnalyzer = jest.spyOn(annotationAnalyzer, 'transform') - spyOnAnnotationAnalyzer.mockImplementation(async(system) => system) - const excludedNodesRemover = app.get(ExcludedNodesRemover) const spyOnExcludedNodesRemover = jest.spyOn(excludedNodesRemover, 'transform') spyOnExcludedNodesRemover.mockImplementation(async(system) => system) @@ -69,7 +64,6 @@ describe(CollectorService.name, () => { expect(spyOnRabbitMqCollector).toHaveBeenCalled() expect(spyOnFeignProducer).toHaveBeenCalled() - expect(spyOnAnnotationAnalyzer).toHaveBeenCalled() expect(spyOnExcludedNodesRemover).toHaveBeenCalled() expect(spyOnCabinetLabelsTransformer).toHaveBeenCalled() diff --git a/sources/acmi-analyzer/src/collector/Collector.service.ts b/sources/acmi-analyzer/src/collector/Collector.service.ts index 2c33189..9237273 100644 --- a/sources/acmi-analyzer/src/collector/Collector.service.ts +++ b/sources/acmi-analyzer/src/collector/Collector.service.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common' +import { ModuleRef } from '@nestjs/core' import * as _ from 'lodash' import { System, MicroService } from '../model/ms' -import { ModuleRef } from '@nestjs/core' import { KubernetesCollectorService } from '../kubernetes/collector/KubernetesCollector.service' import { RabbitMqCollectorService } from '../rabbitmq/collector/RabbitMqCollector.service' @@ -26,7 +26,6 @@ export class CollectorService { system = await this.rabbitMqCollector.transform(system) system = await this.getProvider('FeignClientsFromSourceCodeProducer').transform(system) - system = await this.getProvider('AnnotationAnalyzer').transform(system) system = await this.getProvider('SourceLocationDecorator').transform(system) // INFO: actually this is no collector. we keep here for compatibility reasons diff --git a/sources/acmi-analyzer/src/collector/java-source/AnnotationAnalyzer.service.test.ts b/sources/acmi-analyzer/src/collector/java-source/AnnotationAnalyzer.service.test.ts index 5ca2f23..7e84ed9 100644 --- a/sources/acmi-analyzer/src/collector/java-source/AnnotationAnalyzer.service.test.ts +++ b/sources/acmi-analyzer/src/collector/java-source/AnnotationAnalyzer.service.test.ts @@ -4,7 +4,7 @@ import { ConfigService } from '../../config/Config.service' import { System, AsyncEventFlow } from '../../model/ms' import { verifyEachContentHasTransformer } from '../../test/verifiers' -import { AnnotationAnalyzer } from './AnnotationAnalyzer.service' +import { AnnotationAnalyzer, ElementMapping } from './AnnotationAnalyzer.service' describe(AnnotationAnalyzer.name, () => { let app: TestingModule @@ -25,6 +25,21 @@ describe(AnnotationAnalyzer.name, () => { }).compile() }) + const elementMappings: ElementMapping[] = [ + { + elementName: 'sendToExchange', + nodeTypeToCreate: 'MessageExchange', + nodeTypeDirection: 'target', + edgeType: 'AsyncEventFlow' + }, + { + elementName: 'receiveFromExchange', + nodeTypeToCreate: 'MessageExchange', + nodeTypeDirection: 'source', + edgeType: 'AsyncEventFlow' + } + ] + it('creates an async info flow for multiple annotations in the same file', async() => { const config = app.get(ConfigService) jest.spyOn(config, 'getSourceFolder').mockImplementation( @@ -35,7 +50,7 @@ describe(AnnotationAnalyzer.name, () => { inputSystem.addMicroService('service1') const transformer = app.get(AnnotationAnalyzer) - const outputSystem = await transformer.transform(inputSystem) + const outputSystem = await transformer.transform(inputSystem, 'EventProcessor', elementMappings) expect(outputSystem.findMicroService('service1')).toBeDefined() expect(outputSystem.findMessageExchange('target-exchange-X')).toBeDefined() @@ -63,7 +78,7 @@ describe(AnnotationAnalyzer.name, () => { inputSystem.addMessageExchange('source-exchange-X') const transformer = app.get(AnnotationAnalyzer) - const outputSystem = await transformer.transform(inputSystem) + const outputSystem = await transformer.transform(inputSystem, 'EventProcessor', elementMappings) expect(outputSystem.findMicroService('service1')).toBeDefined() expect(outputSystem.nodes.filter(node => node.getName() === 'source-exchange-X')).toHaveLength(1) @@ -80,7 +95,7 @@ describe(AnnotationAnalyzer.name, () => { const inputSystem = new System('test') const transformer = app.get(AnnotationAnalyzer) - const outputSystem = await transformer.transform(inputSystem) + const outputSystem = await transformer.transform(inputSystem, 'EventProcessor', elementMappings) expect(outputSystem.findMicroService('service1')).toBeUndefined() expect(outputSystem.findMessageExchange('target-exchange')).toBeUndefined() @@ -96,7 +111,7 @@ describe(AnnotationAnalyzer.name, () => { inputSystem.addMicroService('service1') const transformer = app.get(AnnotationAnalyzer) - const outputSystem = await transformer.transform(inputSystem) + const outputSystem = await transformer.transform(inputSystem, 'EventProcessor', elementMappings) expect(outputSystem.findMicroService('service1')).toBeDefined() expect(outputSystem.findMessageExchange('source-exchange-X')).toBeDefined() diff --git a/sources/acmi-analyzer/src/collector/java-source/AnnotationAnalyzer.service.ts b/sources/acmi-analyzer/src/collector/java-source/AnnotationAnalyzer.service.ts index 6a72743..4aef6d1 100644 --- a/sources/acmi-analyzer/src/collector/java-source/AnnotationAnalyzer.service.ts +++ b/sources/acmi-analyzer/src/collector/java-source/AnnotationAnalyzer.service.ts @@ -21,12 +21,12 @@ export class AnnotationAnalyzer { private readonly config: ConfigService ) { } - public async transform(system: System): Promise { - await this.transformSourcesInPath(system, this.config.getSourceFolder()) + public async transform(system: System, annotation: string, elementMappings: ElementMapping[]): Promise { + await this.transformSourcesInPath(system, this.config.getSourceFolder(), annotation, elementMappings) return system } - private async transformSourcesInPath(system: System, path: string) { + private async transformSourcesInPath(system: System, path: string, annotation: string, elementMappings: ElementMapping[]) { this.logger.log('scanning java files in ' + path) const javaFiles = await findFiles(path, '.java') this.logger.log('found ' + javaFiles.length + ' java files') @@ -42,28 +42,13 @@ export class AnnotationAnalyzer { } else { const fileContent = fs.readFileSync(file, 'utf8') - const elementMappings: ElementMapping[] = [ - { - elementName: 'sendToExchange', - nodeTypeToCreate: 'MessageExchange', - nodeTypeDirection: 'target', - edgeType: 'AsyncEventFlow' - }, - { - elementName: 'receiveFromExchange', - nodeTypeToCreate: 'MessageExchange', - nodeTypeDirection: 'source', - edgeType: 'AsyncEventFlow' - } - ] - - transformEachAnnotation(system, service, 'EventProcessor', fileContent, elementMappings) + transformEachAnnotation(system, service, annotation, fileContent, elementMappings) } }) } } -type ElementMapping = { +export type ElementMapping = { /** * name of the annotation element that defines the node name as its value */ diff --git a/sources/acmi-analyzer/src/collector/transformer.module.ts b/sources/acmi-analyzer/src/collector/transformer.module.ts index 07f4947..ad4bf62 100644 --- a/sources/acmi-analyzer/src/collector/transformer.module.ts +++ b/sources/acmi-analyzer/src/collector/transformer.module.ts @@ -6,7 +6,7 @@ import { CabinetTransformer } from './generic-transformer/CabinetTransformer' import { MicroserviceWithMessageExchangeMerger } from './generic-transformer/MicroserviceWithMessageExchangeMerger' import { ConfigModule } from '../config/config.module' import { SourceCodeAnalysisModule } from '../source-code-analysis/SourceCodeAnalysis.module' -import { AnnotationAnalyzer } from './java-source/AnnotationAnalyzer.service' +import { AnnotationAnalyzer, ElementMapping } from './java-source/AnnotationAnalyzer.service' @Module({ imports: [ @@ -33,6 +33,6 @@ import { AnnotationAnalyzer } from './java-source/AnnotationAnalyzer.service' class TransformerModule { } export { - TransformerModule, ExcludedNodesRemover, FeignClientsFromSourceCodeProducer, AnnotationAnalyzer, + TransformerModule, ExcludedNodesRemover, FeignClientsFromSourceCodeProducer, AnnotationAnalyzer, ElementMapping, CabinetTransformer, MicroserviceWithMessageExchangeMerger }