Skip to content
Closed
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
2 changes: 2 additions & 0 deletions cli/azd/.vscode/cspell.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ words:
- runcontext
- unmarshals
- usgovcloudapi
- jdbc
- postgre
languageSettings:
- languageId: go
ignoreRegExpList:
Expand Down
8 changes: 8 additions & 0 deletions cli/azd/internal/appdetect/appdetect.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ func (db DatabaseDep) Display() string {
return ""
}

type Metadata struct {
ApplicationName string
DatabaseNameInPropertySpringDatasourceUrl map[DatabaseDep]string
}

type Project struct {
// The language associated with the project.
Language Language
Expand All @@ -141,6 +146,9 @@ type Project struct {
// Experimental: Database dependencies inferred through heuristics while scanning dependencies in the project.
DatabaseDeps []DatabaseDep

// Experimental: Metadata inferred through heuristics while scanning the project.
Metadata Metadata

// The path to the project directory.
Path string

Expand Down
90 changes: 73 additions & 17 deletions cli/azd/internal/repository/app_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"maps"
"os"
"path/filepath"
"regexp"
"slices"
"strings"
"time"
Expand Down Expand Up @@ -424,31 +425,34 @@ func (i *Initializer) prjConfigFromDetect(
continue
}

var err error
databaseName, err := getDatabaseName(database, &detect, i.console, ctx)
if err != nil {
return config, err
}

if database == appdetect.DbMongo {
mongo := project.ResourceConfig{
Type: project.ResourceTypeDbMongo,
Name: "mongo",
Props: project.MongoDBProps{
DatabaseName: databaseName,
},
}
config.Resources[mongo.Name] = &mongo
dbNames[database] = mongo.Name
continue
}

var dbType project.ResourceType
switch database {
case appdetect.DbMongo:
dbType = project.ResourceTypeDbMongo
case appdetect.DbPostgres:
dbType = project.ResourceTypeDbPostgres
}

db := project.ResourceConfig{
Type: dbType,
}

for {
dbName, err := promptDbName(i.console, ctx, database)
if err != nil {
return config, err
}

if dbName == "" {
i.console.Message(ctx, "Database name is required.")
continue
}

db.Name = dbName
break
Name: databaseName,
}

config.Resources[db.Name] = &db
Expand Down Expand Up @@ -578,3 +582,55 @@ func ServiceFromDetect(

return svc, nil
}

func getDatabaseName(database appdetect.DatabaseDep, detect *detectConfirm,
console input.Console, ctx context.Context) (string, error) {
dbName := getDatabaseNameFromProjectMetadata(detect, database)
if dbName != "" {
return dbName, nil
}
for {
dbName, err := console.Prompt(ctx, input.ConsoleOptions{
Message: fmt.Sprintf("Input the databaseName for %s "+
"(Not databaseServerName. This url can explain the difference: "+
"'jdbc:mysql://databaseServerName:3306/databaseName'):", database.Display()),
Help: "Hint: App database name\n\n" +
"Name of the database that the app connects to. " +
"This database will be created after running azd provision or azd up.\n" +
"You may be able to skip this step by hitting enter, in which case the database will not be created.",
})
if err != nil {
return "", err
}
if isValidDatabaseName(dbName) {
return dbName, nil
} else {
console.Message(ctx, "Invalid database name. Please choose another name.")
}
}
}

func getDatabaseNameFromProjectMetadata(detect *detectConfirm, database appdetect.DatabaseDep) string {
result := ""
for _, service := range detect.Services {
// todo this should not be here, it should be part of the app detect
name := service.Metadata.DatabaseNameInPropertySpringDatasourceUrl[database]
if name != "" {
if result == "" {
result = name
} else {
// different project configured different db name, not use any of them.
return ""
}
}
}
return result
}

func isValidDatabaseName(name string) bool {
if len(name) < 3 || len(name) > 63 {
return false
}
re := regexp.MustCompile(`^[a-z0-9]+(-[a-z0-9]+)*$`)
return re.MatchString(name)
}
9 changes: 6 additions & 3 deletions cli/azd/internal/repository/app_init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,12 @@ func TestInitializer_prjConfigFromDetect(t *testing.T) {
Type: project.ResourceTypeDbRedis,
Name: "redis",
},
"mongodb": {
"mongo": {
Type: project.ResourceTypeDbMongo,
Name: "mongodb",
Name: "mongo",
Props: project.MongoDBProps{
DatabaseName: "mongodb",
},
},
"postgres": {
Type: project.ResourceTypeDbPostgres,
Expand All @@ -247,7 +250,7 @@ func TestInitializer_prjConfigFromDetect(t *testing.T) {
"py": {
Type: project.ResourceTypeHostContainerApp,
Name: "py",
Uses: []string{"postgres", "mongodb", "redis"},
Uses: []string{"postgres", "mongo", "redis"},
Props: project.ContainerAppProps{
Port: 80,
},
Expand Down
24 changes: 24 additions & 0 deletions cli/azd/internal/repository/detect_confirm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,30 @@ func Test_detectConfirm_confirm(t *testing.T) {
},
},
},
{
name: "confirm single with cosmos db mongo resource",
detection: []appdetect.Project{
{
Language: appdetect.Java,
Path: javaDir,
DatabaseDeps: []appdetect.DatabaseDep{
appdetect.DbMongo,
},
},
},
interactions: []string{
"Confirm and continue initializing my app",
},
want: []appdetect.Project{
{
Language: appdetect.Java,
Path: javaDir,
DatabaseDeps: []appdetect.DatabaseDep{
appdetect.DbMongo,
},
},
},
},
{
name: "add a language",
detection: []appdetect.Project{
Expand Down
16 changes: 7 additions & 9 deletions cli/azd/internal/repository/infra_confirm.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,17 @@ func (i *Initializer) infraSpecFromDetect(

switch db {
case appdetect.DbMongo:
serviceSpec.DbCosmosMongo = &scaffold.DatabaseReference{
DatabaseName: spec.DbCosmosMongo.DatabaseName,
}
err = scaffold.BindToMongoDb(&serviceSpec, spec.DbCosmosMongo)
case appdetect.DbPostgres:
serviceSpec.DbPostgres = &scaffold.DatabaseReference{
DatabaseName: spec.DbPostgres.DatabaseName,
}
err = scaffold.BindToPostgres(&serviceSpec, spec.DbPostgres)
case appdetect.DbRedis:
serviceSpec.DbRedis = &scaffold.DatabaseReference{
DatabaseName: "redis",
}
err = scaffold.BindToRedis(&serviceSpec, spec.DbRedis)
}
}

if err != nil {
return scaffold.InfraSpec{}, err
}
spec.Services = append(spec.Services, serviceSpec)
}

Expand Down
41 changes: 40 additions & 1 deletion cli/azd/internal/repository/infra_confirm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
)

func TestInitializer_infraSpecFromDetect(t *testing.T) {
envsForPostgres, _ := scaffold.GetServiceBindingEnvsForPostgres()
envsForCosmosMongo := scaffold.GetServiceBindingEnvsForMongo()
tests := []struct {
name string
detect detectConfirm
Expand Down Expand Up @@ -93,6 +95,42 @@ func TestInitializer_infraSpecFromDetect(t *testing.T) {
},
},
},
{
name: "api with cosmos db",
detect: detectConfirm{
Services: []appdetect.Project{
{
Language: appdetect.Java,
Path: "java",
DatabaseDeps: []appdetect.DatabaseDep{
appdetect.DbMongo,
},
},
},
Databases: map[appdetect.DatabaseDep]EntryKind{
appdetect.DbMongo: EntryKindDetected,
},
},
interactions: []string{
"cosmos-db-mongo-name",
},
want: scaffold.InfraSpec{
Services: []scaffold.ServiceSpec{
{
Name: "java",
Port: 8080,
Backend: &scaffold.Backend{},
DbCosmosMongo: &scaffold.DatabaseCosmosMongo{
DatabaseName: "cosmos-db-mongo-name",
},
Envs: envsForCosmosMongo,
},
},
DbCosmosMongo: &scaffold.DatabaseCosmosMongo{
DatabaseName: "cosmos-db-mongo-name",
},
},
},
{
name: "api and web",
detect: detectConfirm{
Expand Down Expand Up @@ -183,9 +221,10 @@ func TestInitializer_infraSpecFromDetect(t *testing.T) {
},
},
},
DbPostgres: &scaffold.DatabaseReference{
DbPostgres: &scaffold.DatabasePostgres{
DatabaseName: "myappdb",
},
Envs: envsForPostgres,
},
{
Name: "js",
Expand Down
Loading