Skip to content

Commit

Permalink
refactor the java analyzer
Browse files Browse the repository at this point in the history
  • Loading branch information
saragluna committed Sep 26, 2024
1 parent 6587f83 commit 103a005
Show file tree
Hide file tree
Showing 17 changed files with 409 additions and 137 deletions.
18 changes: 9 additions & 9 deletions cli/azd/internal/appdetect/appdetect.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,22 +354,22 @@ func analyze(projects []Project) []Project {
return result
}

func enrichFromJavaProject(javaProject javaanalyze.JavaProject, project *Project) {
func enrichFromJavaProject(azureYaml javaanalyze.AzureYaml, project *Project) {
// if there is only one project, we can safely assume that it is the main project
for _, resource := range javaProject.Resources {
if resource.Type == "Azure Storage" {
for _, resource := range azureYaml.Resources {
if resource.GetType() == "Azure Storage" {
// project.DatabaseDeps = append(project.DatabaseDeps, Db)
} else if resource.Type == "MySQL" {
} else if resource.GetType() == "MySQL" {
project.DatabaseDeps = append(project.DatabaseDeps, DbMySql)
} else if resource.Type == "PostgreSQL" {
} else if resource.GetType() == "PostgreSQL" {
project.DatabaseDeps = append(project.DatabaseDeps, DbPostgres)
} else if resource.Type == "SQL Server" {
} else if resource.GetType() == "SQL Server" {
project.DatabaseDeps = append(project.DatabaseDeps, DbSqlServer)
} else if resource.Type == "Redis" {
} else if resource.GetType() == "Redis" {
project.DatabaseDeps = append(project.DatabaseDeps, DbRedis)
} else if resource.Type == "Azure Service Bus" {
} else if resource.GetType() == "Azure Service Bus" {
project.AzureDeps = append(project.AzureDeps, AzureServiceBus)
} else if resource.Type == "Azure Storage" {
} else if resource.GetType() == "Azure Storage" {
project.AzureDeps = append(project.AzureDeps, AzureStorage)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,47 @@
package javaanalyze

type JavaProject struct {
type AzureYaml struct {
Service *Service `json:"service"`
Resources []Resource `json:"resources"`
Resources []IResource `json:"resources"`
ServiceBindings []ServiceBinding `json:"serviceBindings"`
}

type IResource interface {
GetName() string
GetType() string
GetBicepParameters() []BicepParameter
GetBicepProperties() []BicepProperty
}

type Resource struct {
Name string `json:"name"`
Type string `json:"type"`
BicepParameters []BicepParameter `json:"bicepParameters"`
BicepProperties []BicepProperty `json:"bicepProperties"`
}

func (r *Resource) GetName() string {
return r.Name
}

func (r *Resource) GetType() string {
return r.Type
}

func (r *Resource) GetBicepParameters() []BicepParameter {
return r.BicepParameters
}

func (r *Resource) GetBicepProperties() []BicepProperty {
return r.BicepProperties
}

type ServiceBusResource struct {
Resource
Queues []string `json:"queues"`
TopicAndSubscriptions []string `json:"topicAndSubscriptions"`
}

type BicepParameter struct {
Name string `json:"name"`
Description string `json:"description"`
Expand Down
41 changes: 0 additions & 41 deletions cli/azd/internal/appdetect/javaanalyze/java_analyzer.go

This file was deleted.

38 changes: 38 additions & 0 deletions cli/azd/internal/appdetect/javaanalyze/project_analyzer_java.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package javaanalyze

import "os"

type javaProject struct {
springProject springProject
mavenProject mavenProject
}

func Analyze(path string) []AzureYaml {
var result []AzureYaml
rules := []rule{
&ruleService{},
&ruleMysql{},
&ruleStorage{},
&ruleServiceBusScsb{},
}

entries, err := os.ReadDir(path)
if err == nil {
for _, entry := range entries {
if "pom.xml" == entry.Name() {
mavenProjects, _ := analyzeMavenProject(path)

for _, mavenProject := range mavenProjects {
javaProject := &javaProject{
mavenProject: mavenProject,
springProject: analyzeSpringProject(mavenProject.path),
}
azureYaml, _ := applyRules(javaProject, rules)
result = append(result, *azureYaml)
}
}
}
}

return result
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,54 +5,71 @@ import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
)

// MavenProject represents the top-level structure of a Maven POM file.
type MavenProject struct {
XMLName xml.Name `xml:"project"`
Parent Parent `xml:"parent"`
// mavenProject represents the top-level structure of a Maven POM file.
type mavenProject struct {
XmlName xml.Name `xml:"project"`
Parent parent `xml:"parent"`
Modules []string `xml:"modules>module"` // Capture the modules
Dependencies []Dependency `xml:"dependencies>dependency"`
DependencyManagement DependencyManagement `xml:"dependencyManagement"`
Build Build `xml:"build"`
Path string
Dependencies []dependency `xml:"dependencies>dependency"`
DependencyManagement dependencyManagement `xml:"dependencyManagement"`
Build build `xml:"build"`
path string
spring springProject
}

// Parent represents the parent POM if this project is a module.
type Parent struct {
type parent struct {
GroupId string `xml:"groupId"`
ArtifactId string `xml:"artifactId"`
Version string `xml:"version"`
}

// Dependency represents a single Maven dependency.
type Dependency struct {
type dependency struct {
GroupId string `xml:"groupId"`
ArtifactId string `xml:"artifactId"`
Version string `xml:"version"`
Scope string `xml:"scope,omitempty"`
}

// DependencyManagement includes a list of dependencies that are managed.
type DependencyManagement struct {
Dependencies []Dependency `xml:"dependencies>dependency"`
type dependencyManagement struct {
Dependencies []dependency `xml:"dependencies>dependency"`
}

// Build represents the build configuration which can contain plugins.
type Build struct {
Plugins []Plugin `xml:"plugins>plugin"`
type build struct {
Plugins []plugin `xml:"plugins>plugin"`
}

// Plugin represents a build plugin.
type Plugin struct {
type plugin struct {
GroupId string `xml:"groupId"`
ArtifactId string `xml:"artifactId"`
Version string `xml:"version"`
//Configuration xml.Node `xml:"configuration"`
}

// ParsePOM Parse the POM file.
func ParsePOM(filePath string) (*MavenProject, error) {
func analyzeMavenProject(projectPath string) ([]mavenProject, error) {
rootProject, _ := analyze(projectPath + "/pom.xml")
var result []mavenProject

// if it has submodules
if len(rootProject.Modules) > 0 {
for _, m := range rootProject.Modules {
subModule, _ := analyze(projectPath + "/" + m + "/pom.xml")
result = append(result, *subModule)
}
} else {
result = append(result, *rootProject)
}
return result, nil
}

func analyze(filePath string) (*mavenProject, error) {
xmlFile, err := os.Open(filePath)
if err != nil {
return nil, fmt.Errorf("error opening file: %w", err)
Expand All @@ -64,12 +81,12 @@ func ParsePOM(filePath string) (*MavenProject, error) {
return nil, fmt.Errorf("error reading file: %w", err)
}

var project MavenProject
var project mavenProject
if err := xml.Unmarshal(bytes, &project); err != nil {
return nil, fmt.Errorf("error parsing XML: %w", err)
}

project.Path = filePath
project.path = filepath.Dir(filePath)

return &project, nil
}
78 changes: 78 additions & 0 deletions cli/azd/internal/appdetect/javaanalyze/project_analyzer_spring.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package javaanalyze

import (
"fmt"
"gopkg.in/yaml.v3"
"io/ioutil"
"log"
)

type springProject struct {
applicationProperties map[string]interface{}
}

func analyzeSpringProject(projectPath string) springProject {
return springProject{
applicationProperties: findSpringApplicationProperties(projectPath),
}
}

func findSpringApplicationProperties(projectPath string) map[string]interface{} {
yamlFilePath := projectPath + "/src/main/resources/application.yml"
data, err := ioutil.ReadFile(yamlFilePath)
if err != nil {
log.Fatalf("error reading YAML file: %v", err)
}

// Parse the YAML into a yaml.Node
var root yaml.Node
err = yaml.Unmarshal(data, &root)
if err != nil {
log.Fatalf("error unmarshalling YAML: %v", err)
}

result := make(map[string]interface{})
parseYAML("", &root, result)

return result
}

// Recursively parse the YAML and build dot-separated keys into a map
func parseYAML(prefix string, node *yaml.Node, result map[string]interface{}) {
switch node.Kind {
case yaml.DocumentNode:
// Process each document's content
for _, contentNode := range node.Content {
parseYAML(prefix, contentNode, result)
}
case yaml.MappingNode:
// Process key-value pairs in a map
for i := 0; i < len(node.Content); i += 2 {
keyNode := node.Content[i]
valueNode := node.Content[i+1]

// Ensure the key is a scalar
if keyNode.Kind != yaml.ScalarNode {
continue
}

keyStr := keyNode.Value
newPrefix := keyStr
if prefix != "" {
newPrefix = prefix + "." + keyStr
}
parseYAML(newPrefix, valueNode, result)
}
case yaml.SequenceNode:
// Process items in a sequence (list)
for i, item := range node.Content {
newPrefix := fmt.Sprintf("%s[%d]", prefix, i)
parseYAML(newPrefix, item, result)
}
case yaml.ScalarNode:
// If it's a scalar value, add it to the result map
result[prefix] = node.Value
default:
// Handle other node types if necessary
}
}
14 changes: 7 additions & 7 deletions cli/azd/internal/appdetect/javaanalyze/rule_engine.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package javaanalyze

type rule interface {
Match(*MavenProject) bool
Apply(*JavaProject)
match(project *javaProject) bool
apply(azureYaml *AzureYaml)
}

func ApplyRules(mavenProject *MavenProject, rules []rule) (*JavaProject, error) {
javaProject := &JavaProject{}
func applyRules(javaProject *javaProject, rules []rule) (*AzureYaml, error) {
azureYaml := &AzureYaml{}

for _, r := range rules {
if r.Match(mavenProject) {
r.Apply(javaProject)
if r.match(javaProject) {
r.apply(azureYaml)
}
}
return javaProject, nil
return azureYaml, nil
}
Loading

0 comments on commit 103a005

Please sign in to comment.