Closed Halleck45 closed 4 months ago
This tutorial will guide you through the process of adding a new command-line option, --report-json
, to the project. This option will allow users to generate a report in JSON format. Even if you're not familiar with Go, you should be able to follow along.
If there are any typos in this tutorial, I apologize. Please feel free to point them out to me.
This feature would be useful for users who want to process the analysis results programmatically or integrate them with other tools, and is asked in this discussion
Install Golang on your machine. You can download it from the official website.
Open the main.go
file
This file is responsible for loading a configuration file and processing command-line arguments.
You can see that the program uses the cli
package to parse command-line arguments. The cli
package is a popular package for building command-line applications in Go.
For example, the --report-html
option is managed with:
&cli.StringFlag{
Name: "report-html",
Usage: "Generate an HTML report",
Category: "Report",
},
You'll need to add the --report-json
option to the list of command-line options that the program accepts. This is typically done in the function that sets up the command-line parser. Look for a function call that looks something like this:
cli.BoolFlag{
Name: "report-json",
Usage: "Generate a report in JSON format",
}
Back in the code main.go
file. You'll need to check if the --report-json option was provided by the user. You can do this with cCtx.Bool("report-json")
. If it's true, you'll want to generate a report in JSON format.
// Reports
// ...
if cCtx.String("report-json") != "" {
configuration.Reports.Json = cCtx.String("report-json")
}
Open the src/Configuration/Configuration.go
file. This file contains the definition of the Configuration
struct, which holds the program's configuration settings.
Add a new field to the Configuration
struct to store the path to the JSON report file:
type ConfigurationReport struct {
Html string `yaml:"html"`
Markdown string `yaml:"markdown"`
Json bool `yaml:"json"`
}
Open the src/Command/AnalyzeCommand.go
file. This file contains the AnalyzeCommand
struct, which is responsible for running the analysis on the codebase.
Add a call to our future function JsonReportGenerator.Generate
:
// report: json
jsonReportGenerator := JsonReport.NewJsonReportGenerator(v.configuration.Reports.Json)
err = jsonReportGenerator.Generate(allResults, projectAggregated)
if err != nil {
pterm.Error.Println("Cannot generate json report: " + err.Error())
}
Now, you'll need to implement the JsonReportGenerator.Generate
function.
Create a new file called src/Report/JsonReport/JsonReportGenerator.go
and add the following code:
package Report
import (
"encoding/json"
"io/ioutil"
"os"
)
type JsonReportGenerator struct {
ReportPath string
}
// This factory creates a new JsonReportGenerator
func NewJsonReportGenerator(ReportPath string) *JsonReportGenerator {
return &JsonReportGenerator{ReportPath: ReportPath}
}
// Generate generates a JSON report
func (j *JsonReportGenerator) Generate(allResults []Result, projectAggregated AggregatedResult) error {
// This code serializes the results to JSON
jsonReport, err := json.MarshalIndent(allResults, "", " ")
if err != nil {
return err
}
// This code writes the JSON report to a file
err = ioutil.WriteFile(j.ReportPath, jsonReport, os.ModePerm)
if err != nil {
return err
}
return nil
}
You can run the application with the --report-json
option to generate a JSON report.
go run . analyze --report-json=report.json
If all is well, you should see a new file called report.json
in the root directory of the project.
You should add tests to ensure that the JSON report is generated correctly.
Create a new file called src/Report/JsonReport/JsonReportGenerator_test.go
and add the following code:
package Report
import (
"testing"
)
func TestJsonReportGenerator_Generate(t *testing.T) {
func TestGenerate(t *testing.T) {
tests := []struct {
name string
reportPath string
expectError bool
}{
{
name: "Test with valid report path",
reportPath: "/tmp/report.json",
expectError: false,
},
{
name: "Test with empty report path",
reportPath: "",
expectError: false,
},
{
name: "Test with non-writable report path",
reportPath: "/nonexistent/report.json",
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
generator := &NewJsonReportGenerator{ReportPath: tt.reportPath}
files := []*pb.File{}
projectAggregated := Analyzer.ProjectAggregated{}
err := generator.Generate(files, projectAggregated)
if tt.expectError {
if err == nil {
t.Errorf("Expected an error but got none")
}
} else {
if err != nil {
t.Errorf("Did not expect an error but got: %v", err)
} else {
if tt.reportPath != "" {
if _, err := os.Stat(tt.reportPath); os.IsNotExist(err) {
t.Errorf("Report file was not created")
} else {
// cleanup
os.Remove(tt.reportPath)
}
}
}
}
})
}
}
This test is very basic and only checks if the report file is created. You should add more tests to cover edge cases and ensure that the JSON report is generated correctly.
Run your test with the command:
go test ./...
Reuse src/Analyzer/Aggregator.go types? (Struct tags)
@julian776 I forgot that part indeed!
So I think using the ProjectAggregated.Combined
structure might be enough.
However, to avoid having really large content, for each ConcernedFiles
, I think we should only keep the Stmts.Analyze
, which summarizes the analysis, without having the details for all statements.
What do you think?
Hello 👋🏻
It's my first lines of code in Go and i'm stuck on this function :
func (j *JsonReportGenerator) Generate(allResults []Result, projectAggregated AggregatedResult)
allResults
are type in Result
, and projectAggregated
in an AggregatedResult
When I checked the Generate
function in other Generator (like html and markown), projectAggregated
are typed in Analyzer.ProjectAggregated
which from "github.com/halleck45/ast-metrics/src/Analyzer" if i understood.
But i didn’t find the AggregatedResult
or Result
in the Analyzer.
I got the followed error :
src/Report/Json/JsonReportGenerator.go:21:53: undefined: Result
same for AggregatedResult
Thanks !
Hello @Enz000 ,
that's a mistake in my tutorial :/
You should use something like:
func (j *JsonReportGenerator) Generate(files []*pb.File, projectAggregated Analyzer.ProjectAggregated) error {
and import the Analyzer
first:
import (
...
"github.com/halleck45/ast-metrics/src/Analyzer"
...
)
Looks at the src/Report/Html/HtmlReportGenerator.go
file for example
It would be great to have a
--report-json
option. This is not a huge development, and it would be a great first contribution to Golang.So, if you want to learn Golang, this is probably the right opportunity! :heart:
Here is a short tutorial to try to guide you through this task if you are interested.
:pray: Thank you for your help!