diff --git a/architecture-documentation/src/architecture/resources/workspace.dsl b/architecture-documentation/src/architecture/resources/workspace.dsl index 5c5fd959..20c90dee 100644 --- a/architecture-documentation/src/architecture/resources/workspace.dsl +++ b/architecture-documentation/src/architecture/resources/workspace.dsl @@ -9,13 +9,13 @@ workspace "aadarchi-documentation-system" { "aadarchi.maven.pom" "pom.xml" } maven = container "maven" "The maven build engine" "java, maven" - aadarchi_maven_plugin = container "aadarchi-maven-plugin" "" "java, maven-plugin"{ + aadarchi_maven_plugin = container "aadarchi-maven-plugin" { properties { "aadarchi.sequence.generator.with" "true" } maven -> this "Invokes this plugin during build to generate data" } - aadarchi_base = container "base" "" "Java, CDI" { + aadarchi_base = container "base"{ properties { "aadarchi.sequence.generator.with" "true" } @@ -24,7 +24,7 @@ workspace "aadarchi-documentation-system" { aadarchi_maven_plugin -> this } archetype_6 = container "archetype" "" "maven" - architecture_documentation = container "architecture-documentation" "" "java, maven, structurizr, asciidoc" + architecture_documentation = container "architecture-documentation" } person_architect -> archetype_6 "Bootstrap a valid project" person_architect -> maven "Generates documentation" diff --git a/maven-metadata-inferer/pom.xml b/maven-metadata-inferer/pom.xml index 655b4866..745db732 100644 --- a/maven-metadata-inferer/pom.xml +++ b/maven-metadata-inferer/pom.xml @@ -44,11 +44,10 @@ ${project.version} test - - org.json - json - 20230618 + com.fasterxml.jackson.core + jackson-databind + 2.15.2 @@ -66,8 +65,7 @@ Download popular artifacts - - ${project.build.directory}/generated/resources/mvnrepository/artifacts.json + @@ -87,6 +85,8 @@ good --> false + true + true mvnrepository.json diff --git a/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/MavenEnhancer.java b/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/MavenEnhancer.java index 990ed50e..879f014b 100644 --- a/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/MavenEnhancer.java +++ b/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/MavenEnhancer.java @@ -1,7 +1,11 @@ package org.ndx.aadarchi.inferer.maven; +import java.util.Map; + import org.ndx.aadarchi.base.enhancers.ModelElementKeys; +import com.fasterxml.jackson.core.type.TypeReference; + public interface MavenEnhancer { /** * URL of the maven pom the model element represents. @@ -29,4 +33,7 @@ public interface MavenEnhancer { * When set, this allows users to enter a list of profiles names separated by ";" */ String AGILE_ARCHITECTURE_MAVEN_ADDITIONAL_PROFILES = ModelElementKeys.PREFIX+"maven.profiles"; + + String AGILE_ARCHITECTURE_MAVEN_TECHNOLOGIES = ModelElementKeys.PREFIX+"maven.technologies"; + TypeReference> AGILE_ARCHITECTURE_MAVEN_TECHNOLOGIES_TYPE = new TypeReference>() {}; } diff --git a/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/MavenPomDecorator.java b/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/MavenPomDecorator.java index b4d553d2..211af614 100644 --- a/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/MavenPomDecorator.java +++ b/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/MavenPomDecorator.java @@ -1,14 +1,10 @@ package org.ndx.aadarchi.inferer.maven; import java.util.Arrays; -import java.util.HashSet; -import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; -import java.util.TreeSet; import java.util.function.BiFunction; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -17,22 +13,18 @@ import javax.enterprise.context.ApplicationScoped; import javax.enterprise.inject.Default; import javax.inject.Inject; -import javax.inject.Named; import org.apache.commons.vfs2.FileObject; import org.apache.commons.vfs2.FileSystemException; import org.apache.commons.vfs2.FileSystemManager; -import org.apache.maven.model.Dependency; import org.apache.maven.model.IssueManagement; import org.apache.maven.model.Scm; import org.apache.maven.project.MavenProject; -import org.json.JSONObject; import org.ndx.aadarchi.base.enhancers.ModelElementKeys; +import org.ndx.aadarchi.inferer.maven.technologies.TechnologyDecorator; import com.pivovarit.function.ThrowingFunction; import com.pivovarit.function.ThrowingPredicate; -import com.structurizr.model.Component; -import com.structurizr.model.Container; import com.structurizr.model.Element; @Default @@ -42,7 +34,7 @@ public class MavenPomDecorator { @Inject FileSystemManager fileSystemManager; - @Inject @Named(MvnRepositoryArtifactsProducer.MVNREPOSITORY_ARTIFACTS) JSONObject mvnRepositoryArtifacts; + @Inject TechnologyDecorator technologyDecorator; public static void decorateRecursively(MavenProject project, BiFunction, Boolean> consumer) { decorateRecursively(project, new LinkedList(), consumer); @@ -72,7 +64,7 @@ public void decorate(Element element, MavenProject mavenProject) { decorateJavaSource(element, mavenProject); decorateJavaPackage(element, mavenProject); decorateMavenProperties(element, mavenProject); - decorateTechnology(element, mavenProject); + technologyDecorator.decorateTechnology(element, mavenProject); Optional.ofNullable(mavenProject.getDescription()).stream() .forEach(description -> element.setDescription(description.replaceAll("\n", " "))); } @@ -177,98 +169,4 @@ private String findFirstPackageLevelInPath(FileObject root, FileObject current) .toString() .replace('/', '.').replace('\\', '.'); } - - protected static String technologyWithVersionFromProperty(MavenProject mavenProject, String technology, String... propertyNames) { - return technology + Stream.of(propertyNames) - .flatMap(p -> new HashSet(Arrays.asList(p, p.replace('.', '-'), p.replace('-', '.'))).stream()) - .filter(p -> mavenProject.getProperties().containsKey(p)) - .map(p -> mavenProject.getProperties().get(p)) - .map(text -> " "+text) - .findFirst() - .orElse("") - ; - } - - /** - * TODO replace that with usage of {@link #mvnRepositoryArtifacts} - * @param project - * @return - */ - public static Set doDecorateTechnology(MavenProject project) { - Set technologies = new LinkedHashSet(); - switch (project.getPackaging()) { - case "ear": - technologies.add("Java"); - technologies.add("ear"); - break; - case "war": - technologies.add("Java"); - technologies.add("war"); - case "jar": - // If there is a java version property, use it to detect Java version - technologies.add(MavenPomDecorator.technologyWithVersionFromProperty(project, "Java", "java.version", "maven.compiler.target")); - break; - case "pom": - break; - default: - logger.warning(String.format( - "Maven component %s uses packaging %s which we don't know. Please submit a bug to aadarchi-documentation-system to have this particular packaging correctly handled", - project, project.getPackaging())); - } - for (Dependency dependency : (List) project.getDependencies()) { - switch (dependency.getGroupId()) { - case "org.apache.maven": - if("maven-plugin-api".equals(dependency.getArtifactId())) { - technologies.add("maven-plugin"); - } - break; - case "io.quarkus": - technologies.add(MavenPomDecorator.technologyWithVersionFromProperty(project, "Quarkus", "quarkus.platform.version")); - break; - case "org.springframework": - technologies.add("Spring"); - break; - case "org.apache.camel": - technologies.add(MavenPomDecorator.technologyWithVersionFromProperty(project, "Apache Camel", "camel.version")); - break; - case "org.springframework.boot": - technologies.add("Spring Boot"); - break; - case "com.google.gwt": - technologies.add("GWT"); - break; - case "javax.enterprise": - if("cdi-api".equals(dependency.getArtifactId())) { - technologies.add("CDI"); - } - break; - } - } - return technologies; - } - - /** - * @param element element for which we want the technologies - * @param project - * @return a string giving details about important project infos - */ - public void decorateTechnology(Element element, MavenProject project) { - // TODO replace simple set with something more complex - // Maybe a set of artifact objects - Set technologies = new TreeSet(); - decorateRecursively(project, (p, l) -> { - technologies.addAll(MavenPomDecorator.doDecorateTechnology(p)); - // We should explore all parent poms - return true; - }); - String technologiesText = technologies.stream().collect(Collectors.joining(",")); - if(element instanceof Component) { - ((Component) element).setTechnology(technologiesText); - } else if(element instanceof Container) { - ((Container) element).setTechnology(technologiesText); - } - // TODO add a property to element containing the technologies artifact ids - // For later being able to give some details - } - } diff --git a/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/MavenTechnologiesDocumentationEnhancer.java b/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/MavenTechnologiesDocumentationEnhancer.java new file mode 100644 index 00000000..9426a257 --- /dev/null +++ b/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/MavenTechnologiesDocumentationEnhancer.java @@ -0,0 +1,80 @@ +package org.ndx.aadarchi.inferer.maven; + +import java.util.Map; +import java.util.stream.Collectors; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Default; +import javax.inject.Inject; +import javax.inject.Named; + +import org.ndx.aadarchi.base.AgileArchitectureSection; +import org.ndx.aadarchi.base.OutputBuilder; +import org.ndx.aadarchi.base.OutputBuilder.HandledFormat; +import org.ndx.aadarchi.base.enhancers.ModelElementAdapter; +import org.ndx.aadarchi.inferer.maven.technologies.MvnRepositoryArtifact; +import org.ndx.aadarchi.inferer.maven.technologies.MvnRepositoryArtifactsProducer; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.structurizr.model.StaticStructureElement; + +/** + * An enhancer which generates in development environment some text about the + * notable dependencies + */ +@Default @ApplicationScoped +public class MavenTechnologiesDocumentationEnhancer extends ModelElementAdapter { + @Inject @Named(MvnRepositoryArtifactsProducer.MVNREPOSITORY_ARTIFACTS) Map mvnRepositoryArtifacts; + ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public int priority() { + return 10; + } + + @Override + protected void processElement(StaticStructureElement element, OutputBuilder builder) { + if(element.getProperties().containsKey(MavenEnhancer.AGILE_ARCHITECTURE_MAVEN_TECHNOLOGIES)) { + String technologies = element.getProperties().get(MavenEnhancer.AGILE_ARCHITECTURE_MAVEN_TECHNOLOGIES); + // Rehydrate that to have artifacts + try { + Map dependenciesVersions = objectMapper.readValue(technologies, MavenEnhancer.AGILE_ARCHITECTURE_MAVEN_TECHNOLOGIES_TYPE); + if(!dependenciesVersions.isEmpty()) { + writeDependenciesArtifacts(element, dependenciesVersions, builder); + } + } catch (JsonProcessingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + private void writeDependenciesArtifacts(StaticStructureElement element, Map dependenciesVersions, + OutputBuilder builder) { + String text = dependenciesVersions.entrySet().stream() + .map(entry -> Map.entry(mvnRepositoryArtifacts.get(entry.getKey()), entry.getValue())) + .map(entry -> this.toTableRow(entry.getKey(), entry.getValue())) + .collect(Collectors.joining("\n", + "[%autowidth.stretch, cols=\"1a,1a,1a,1a\"]\n|===" + + "\n|Name|Used version|Categories|Description\n\n", + "\n|===")); + builder.writeToOutput(AgileArchitectureSection.development_environment, element, this, OutputBuilder.Format.adoc, + "# Interesting dependencies\n\n" + + text); + } + + private String toTableRow(MvnRepositoryArtifact key, String version) { + StringBuilder returned = new StringBuilder() + .append("|").append(key.page).append("[").append(key.name).append("] "); + returned + .append("|").append(version==null || version.isBlank() ? "{nbsp}" : version); + if(key.versions.containsKey(version)) + returned.append(" (released ").append(key.versions.get(version)).append(")"); + returned + .append("|").append(key.categories.isEmpty() ? "{nbsp}" : key.categories.stream().collect(Collectors.joining())) + .append("|").append(key.description) + ; + return returned.toString(); + } +} diff --git a/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/technologies/MvnRepositoryArtifact.java b/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/technologies/MvnRepositoryArtifact.java new file mode 100644 index 00000000..d961280a --- /dev/null +++ b/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/technologies/MvnRepositoryArtifact.java @@ -0,0 +1,36 @@ +package org.ndx.aadarchi.inferer.maven.technologies; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class MvnRepositoryArtifact { + public final String name; + public final String coordinates; + public String description; + public List categories; + public List tags; + public Map versions; + public String page; + + @JsonCreator + public MvnRepositoryArtifact(@JsonProperty("name") String name, + @JsonProperty("coordinates") String coordinates, + @JsonProperty("description") String description, + @JsonProperty("page") String page, + @JsonProperty("categories") List categories, + @JsonProperty("tags") List tags, + @JsonProperty("versions") Map versions + ) { + super(); + this.name = name; + this.coordinates = coordinates; + this.page = page; + this.description = description; + this.categories = categories; + this.tags = tags; + this.versions = versions; + } +} diff --git a/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/MvnRepositoryArtifactsProducer.java b/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/technologies/MvnRepositoryArtifactsProducer.java similarity index 55% rename from maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/MvnRepositoryArtifactsProducer.java rename to maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/technologies/MvnRepositoryArtifactsProducer.java index 401b15a4..cbd20ab0 100644 --- a/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/MvnRepositoryArtifactsProducer.java +++ b/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/technologies/MvnRepositoryArtifactsProducer.java @@ -1,7 +1,8 @@ -package org.ndx.aadarchi.inferer.maven; +package org.ndx.aadarchi.inferer.maven.technologies; import java.io.IOException; import java.io.InputStream; +import java.util.Map; import java.util.logging.Logger; import javax.enterprise.context.ApplicationScoped; @@ -9,23 +10,29 @@ import javax.inject.Named; import org.apache.commons.io.IOUtils; -import org.json.JSONObject; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; public class MvnRepositoryArtifactsProducer { private static final Logger logger = Logger.getLogger(MvnRepositoryArtifactsProducer.class.getName()); public static final String MVNREPOSITORY_ARTIFACTS = "mvnRepositoryArtifactsMap"; + /** * @see https://stleary.github.io/JSON-java/index.html * @return a JSONObject containing all popular artifacts * @throws IOException */ - @Produces @ApplicationScoped @Named(MVNREPOSITORY_ARTIFACTS) JSONObject createMvnRepositoryArtifacts() throws IOException { + @Produces @ApplicationScoped @Named(MVNREPOSITORY_ARTIFACTS) Map createMvnRepositoryArtifacts() throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); logger.info( String.format("Loading mvnrepository popular artifacts from %s", getClass().getClassLoader().getResource("mvnrepository.json"))); try(InputStream input = getClass().getClassLoader().getResourceAsStream("mvnrepository.json")) { - String text = IOUtils.toString(input, "UTF-8"); - return new JSONObject(text); + return objectMapper.readValue(input, new TypeReference>() {}); } } } diff --git a/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/technologies/TechnologyDecorator.java b/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/technologies/TechnologyDecorator.java new file mode 100644 index 00000000..90d40453 --- /dev/null +++ b/maven-metadata-inferer/src/main/java/org/ndx/aadarchi/inferer/maven/technologies/TechnologyDecorator.java @@ -0,0 +1,152 @@ +package org.ndx.aadarchi.inferer.maven.technologies; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Default; +import javax.inject.Inject; +import javax.inject.Named; + +import org.apache.maven.model.Dependency; +import org.apache.maven.project.MavenProject; +import org.ndx.aadarchi.inferer.maven.MavenEnhancer; +import org.ndx.aadarchi.inferer.maven.MavenPomDecorator; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.structurizr.model.Component; +import com.structurizr.model.Container; +import com.structurizr.model.Element; + +/** + * Component dedicated to technology decoration. + * Given a maven pom and an element, it will detect interesting technologies, + * add them to the element when possible, + * and add them to documentation when possible + */ +@Default +@ApplicationScoped +public class TechnologyDecorator { + @Inject @Named(MvnRepositoryArtifactsProducer.MVNREPOSITORY_ARTIFACTS) Map mvnRepositoryArtifacts; + + ObjectMapper objectMapper = new ObjectMapper(); + + /** + * @param element element for which we want the technologies + * @param project + * @return a string giving details about important project infos + */ + public void decorateTechnology(Element element, MavenProject project) { + Map dependencies = new TreeMap(); + /** + * Unfortunatly, we have to make some cumbersome code to extract versions expressed in dependency management + * (or through properties) + */ + Map managedDependenciesVersions = new TreeMap(); + MavenPomDecorator.decorateRecursively(project, (mavenProject, mavenModules) -> { + if(mavenProject.getDependencyManagement()!=null) { + mavenProject.getDependencyManagement().getDependencies() + .forEach(d -> managedDependenciesVersions.put(d.getGroupId()+"."+d.getArtifactId(), d.getVersion())); + for(String dependencyId : managedDependenciesVersions.keySet()) { + String version = managedDependenciesVersions.get(dependencyId); + String propertyInterpolationStart = "${"; + String propertyInterpolationEnd = "}"; + while(version.contains(propertyInterpolationStart)) { + String property = version.substring(version.indexOf( + propertyInterpolationStart)+propertyInterpolationStart.length(), + version.indexOf(propertyInterpolationEnd)); + if(mavenProject.getProperties().containsKey(property)) { + version = version.replace( + propertyInterpolationStart+property+propertyInterpolationEnd, + mavenProject.getProperties().getProperty(property)); + managedDependenciesVersions.put(dependencyId, version); + } else { + // If this property is unknown, it may well be defined in a parent pom + break; + } + } + } + // Before to write that, let's replace all dependencies versions with the ones we know + for(String dependencyId : dependencies.keySet()) { + if(managedDependenciesVersions.containsKey(dependencyId)) { + dependencies.put(dependencyId, managedDependenciesVersions.get(dependencyId)); + } + } + } + dependencies.putAll(doDecorateTechnologies(mavenProject, element)); + // We should explore all parent poms + return true; + }); + try { + element.addProperty(MavenEnhancer.AGILE_ARCHITECTURE_MAVEN_TECHNOLOGIES, objectMapper.writeValueAsString(dependencies)); + } catch (JsonProcessingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + /** + * Decorate the given element with the given technologies by applying the following steps + *
    + *
  1. Detect technologies in dependencies
  2. + *
  3. Transform them into a set of names
  4. + *
  5. Merge that set of names with the existing technologies of element
  6. + *
+ * @param mavenProject + * @param element + * @return + */ + private Map doDecorateTechnologies(MavenProject mavenProject, Element element) { + Map dependenciesToArtifacts = ((List) mavenProject.getDependencies()).stream() + .filter(d -> mvnRepositoryArtifacts.containsKey(d.getGroupId()+"."+d.getArtifactId())) + .collect(Collectors.toMap(Function.identity(), + d -> mvnRepositoryArtifacts.get(d.getGroupId()+"."+d.getArtifactId()))); + // Now we can map dependencies to artifacts, first put the list of artifact names into technologies + List technologies = dependenciesToArtifacts.values().stream() + // We filter out all technologies tagged with "testing" to simplify things a little in technologies + .filter(a -> !a.tags.contains("testing")) + .map(a -> a.name) + .collect(Collectors.toList()); + // If dependencies are recognized, it means Java is used! + if(!technologies.isEmpty()) { + technologies.add(0, "Java"); + } + injectTechnologiesInElement(element, technologies); + return dependenciesToArtifacts.keySet().stream().collect(Collectors.toMap(d -> d.getGroupId()+"."+d.getArtifactId(), d -> d.getVersion()==null ? "":d.getVersion())); + } + + private void injectTechnologiesInElement(Element element, List technologies) { + // As I don't want to repeat myself, I use the rude way of the method handle + try { + Consumer setter = element instanceof Container ? + ((Container) element)::setTechnology : + element instanceof Component ? ((Component) element)::setTechnology : null; + Supplier getter = element instanceof Container ? + ((Container) element)::getTechnology : + element instanceof Component ? ((Component) element)::getTechnology : null; + if(setter!=null && getter!=null) { + String existingTechnologies = getter.get(); + List existingTechnologiesList = List.of(existingTechnologies==null || existingTechnologies.isBlank() ? new String[0] : existingTechnologies.split(",")); + Set technologiesToInsert = new TreeSet<>(); + technologiesToInsert.addAll(technologies); + // Little problem : this won't make Java appear first, maybe a solution with some kind + // of virtual artifact for the JVM and sorting based upon popularity would do the trick + technologiesToInsert.addAll(existingTechnologiesList); + String technologiesText = technologiesToInsert.stream().collect(Collectors.joining(",")); + setter.accept(technologiesText); + } + } catch (SecurityException | IllegalArgumentException e) { + // Nothing to do, because some elements of the Structurizr model don't have any technology declared + } + } +} diff --git a/maven-metadata-inferer/src/test/java/org/ndx/aadarchi/inferer/maven/MavenDetailsInfererEnhancerTest.java b/maven-metadata-inferer/src/test/java/org/ndx/aadarchi/inferer/maven/MavenDetailsInfererEnhancerTest.java index 9ced524c..0329cc95 100644 --- a/maven-metadata-inferer/src/test/java/org/ndx/aadarchi/inferer/maven/MavenDetailsInfererEnhancerTest.java +++ b/maven-metadata-inferer/src/test/java/org/ndx/aadarchi/inferer/maven/MavenDetailsInfererEnhancerTest.java @@ -44,6 +44,7 @@ public class MavenDetailsInfererEnhancerTest { .containsOnlyKeys( ModelElementKeys.ConfigProperties.BasePath.NAME, MavenEnhancer.AGILE_ARCHITECTURE_MAVEN_COORDINATES, + MavenEnhancer.AGILE_ARCHITECTURE_MAVEN_TECHNOLOGIES, ModelElementKeys.Scm.PROJECT, ModelElementKeys.ISSUE_MANAGER ); @@ -64,6 +65,7 @@ public class MavenDetailsInfererEnhancerTest { ModelElementKeys.Scm.PATH, MavenEnhancer.AGILE_ARCHITECTURE_MAVEN_COORDINATES, MavenEnhancer.AGILE_ARCHITECTURE_MAVEN_POM, + MavenEnhancer.AGILE_ARCHITECTURE_MAVEN_TECHNOLOGIES, ModelElementKeys.Scm.PROJECT, ModelElementKeys.JAVA_SOURCES, ModelElementKeys.JAVA_PACKAGES, diff --git a/maven-metadata-inferer/src/test/java/org/ndx/aadarchi/inferer/maven/MavenPomDecoratorTest.java b/maven-metadata-inferer/src/test/java/org/ndx/aadarchi/inferer/maven/MavenPomDecoratorTest.java index 0605cbb8..4fd68336 100644 --- a/maven-metadata-inferer/src/test/java/org/ndx/aadarchi/inferer/maven/MavenPomDecoratorTest.java +++ b/maven-metadata-inferer/src/test/java/org/ndx/aadarchi/inferer/maven/MavenPomDecoratorTest.java @@ -37,6 +37,7 @@ public void can_decorate_software_system() { Assertions.assertThat(system.getProperties()) .containsOnlyKeys( MavenEnhancer.AGILE_ARCHITECTURE_MAVEN_COORDINATES, + MavenEnhancer.AGILE_ARCHITECTURE_MAVEN_TECHNOLOGIES, ModelElementKeys.Scm.PROJECT, ModelElementKeys.ISSUE_MANAGER ); @@ -53,11 +54,14 @@ public void can_decorate_container() { // When decorator.decorate(container, project); // Then - // Don't forget that mavenpomdecorator doesn't independently update used technologies Assertions.assertThat(container.getDescription()).isNotNull(); + Assertions.assertThat(container.getTechnology()) + .isNotNull() + .containsIgnoringCase("maven"); Assertions.assertThat(container.getProperties()) .containsOnlyKeys( MavenEnhancer.AGILE_ARCHITECTURE_MAVEN_COORDINATES, + MavenEnhancer.AGILE_ARCHITECTURE_MAVEN_TECHNOLOGIES, ModelElementKeys.Scm.PROJECT, ModelElementKeys.JAVA_SOURCES, ModelElementKeys.JAVA_PACKAGES, diff --git a/maven-metadata-inferer/src/test/java/org/ndx/aadarchi/inferer/maven/MavenTechnologiesDocumentationEnhancerTest.java b/maven-metadata-inferer/src/test/java/org/ndx/aadarchi/inferer/maven/MavenTechnologiesDocumentationEnhancerTest.java new file mode 100644 index 00000000..76f32594 --- /dev/null +++ b/maven-metadata-inferer/src/test/java/org/ndx/aadarchi/inferer/maven/MavenTechnologiesDocumentationEnhancerTest.java @@ -0,0 +1,68 @@ +package org.ndx.aadarchi.inferer.maven; + +import java.util.Arrays; +import java.util.Map; + +import javax.inject.Inject; + +import org.apache.commons.vfs2.FileObject; +import org.apache.commons.vfs2.FileSystemException; +import org.apache.deltaspike.core.api.config.ConfigProperty; +import org.assertj.core.api.Assertions; +import org.assertj.core.api.InstanceOfAssertFactories; +import org.jboss.weld.junit5.EnableWeld; +import org.jboss.weld.junit5.WeldInitiator; +import org.jboss.weld.junit5.WeldSetup; +import org.junit.jupiter.api.Test; +import org.ndx.aadarchi.base.ArchitectureEnhancer; +import org.ndx.aadarchi.base.enhancers.ModelElementKeys; +import org.ndx.aadarchi.base.enhancers.ModelElementKeys.ConfigProperties.BasePath; +import org.ndx.aadarchi.base.utils.commonsvfs.FileObjectDetector; +import org.ndx.aadarchi.inferer.maven.technologies.MvnRepositoryArtifactsProducer; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.structurizr.Workspace; +import com.structurizr.model.Container; +import com.structurizr.model.SoftwareSystem; + +@EnableWeld +public class MavenTechnologiesDocumentationEnhancerTest { + @WeldSetup + public WeldInitiator weld = WeldInitiator.performDefaultDiscovery(); + + @Inject MavenTechnologiesDocumentationEnhancer tested; + @Inject MavenDetailsInfererEnhancer mavenReader; + @Inject ArchitectureEnhancer enhancer; + + @Inject @ConfigProperty(name=BasePath.NAME, defaultValue = BasePath.VALUE) FileObject basePath; + + @Test public void can_detect_dependencies_versions_declared_through_dependency_management_and_properties() throws FileSystemException, JsonMappingException, JsonProcessingException { + // Given + var w = new Workspace(getClass().getName(), "a test workspace"); + SoftwareSystem system = w.getModel().addSoftwareSystem("The system to decorate with maven informations"); + system.addProperty(ModelElementKeys.ConfigProperties.BasePath.NAME, basePath.getName().getPath()); + // When + // We emulate in-depth visit (but do not really perform it) + enhancer.enhance(w, Arrays.asList(mavenReader, tested)); + // Then + // There are containers in system + Assertions.assertThat(system.getContainers()).isNotEmpty(); + Container mavenMetadataInferer = system.getContainerWithName("maven-metadata-inferer"); + // For this container, we will try to see what versions are given for some dependencies + Assertions.assertThat(mavenMetadataInferer.getProperties()) + .containsKey(MavenEnhancer.AGILE_ARCHITECTURE_MAVEN_TECHNOLOGIES); + // Now extract the dependencies + ObjectMapper objectMapper = new ObjectMapper(); + Map dependenciesVersions = objectMapper.readValue( + mavenMetadataInferer.getProperties().get(MavenEnhancer.AGILE_ARCHITECTURE_MAVEN_TECHNOLOGIES) + , MavenEnhancer.AGILE_ARCHITECTURE_MAVEN_TECHNOLOGIES_TYPE); + // Yeah, i'm searching for the dependency that make that very line of code possible + // So it should not fail + Assertions.assertThat(dependenciesVersions) + .extractingByKey("org.assertj.assertj-core") + .isEqualTo("3.23.1"); + Assertions.assertThat(mavenMetadataInferer.getTechnology()).doesNotStartWith(","); + } +} diff --git a/maven-metadata-inferer/src/test/java/org/ndx/aadarchi/inferer/maven/MvnRepositoryArtifactsProducerTest.java b/maven-metadata-inferer/src/test/java/org/ndx/aadarchi/inferer/maven/technologies/MvnRepositoryArtifactsProducerTest.java similarity index 53% rename from maven-metadata-inferer/src/test/java/org/ndx/aadarchi/inferer/maven/MvnRepositoryArtifactsProducerTest.java rename to maven-metadata-inferer/src/test/java/org/ndx/aadarchi/inferer/maven/technologies/MvnRepositoryArtifactsProducerTest.java index 8f9e0993..1125e96c 100644 --- a/maven-metadata-inferer/src/test/java/org/ndx/aadarchi/inferer/maven/MvnRepositoryArtifactsProducerTest.java +++ b/maven-metadata-inferer/src/test/java/org/ndx/aadarchi/inferer/maven/technologies/MvnRepositoryArtifactsProducerTest.java @@ -1,6 +1,6 @@ -package org.ndx.aadarchi.inferer.maven; +package org.ndx.aadarchi.inferer.maven.technologies; -import static org.junit.jupiter.api.Assertions.fail; +import java.util.Map; import javax.inject.Inject; import javax.inject.Named; @@ -9,7 +9,6 @@ import org.jboss.weld.junit5.EnableWeld; import org.jboss.weld.junit5.WeldInitiator; import org.jboss.weld.junit5.WeldSetup; -import org.json.JSONObject; import org.junit.jupiter.api.Test; @EnableWeld @@ -18,13 +17,18 @@ class MvnRepositoryArtifactsProducerTest { @WeldSetup public WeldInitiator weld = WeldInitiator.performDefaultDiscovery(); - @Inject JSONObject data; -/* + @Inject @Named(MvnRepositoryArtifactsProducer.MVNREPOSITORY_ARTIFACTS) Map data; + @Test void mvnrepository_has_some_artifacts_in() { Assertions.assertThat(data).isNotNull(); - Assertions.assertThat(data.toMap()).isNotEmpty(); - + Assertions.assertThat(data).isNotEmpty(); + Assertions.assertThat(data) + .containsKey("junit.junit") + .extractingByKey("junit.junit") + .hasFieldOrPropertyWithValue("name", "JUnit") + .hasFieldOrPropertyWithValue("coordinates", "junit.junit") + ; } -*/ + }