Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
402ea93
Create application command
asafgabai May 19, 2025
81bada5
Create application command
asafgabai May 19, 2025
bb83487
Create application command
asafgabai May 19, 2025
9a463db
Create application command
asafgabai May 19, 2025
9f1939d
Create application command
asafgabai May 19, 2025
9edf5a4
Create application command
asafgabai May 19, 2025
3ef91d9
Static analysis fixes
asafgabai May 19, 2025
f2106be
Create application command
asafgabai May 20, 2025
d6552a1
Create application command
asafgabai May 20, 2025
362f730
Create application command
asafgabai May 20, 2025
730d870
Create application command
asafgabai May 20, 2025
2a2d746
Update application command
asafgabai May 20, 2025
85abb33
Update application command
asafgabai May 20, 2025
a38c311
Create application command
asafgabai May 20, 2025
9f706be
Create application command
asafgabai May 20, 2025
f031ec9
Merge remote-tracking branch 'origin/APP-440-Create-application-descr…
asafgabai May 20, 2025
064c2ac
Update application command
asafgabai May 20, 2025
e7f7c58
Create application command
asafgabai May 20, 2025
2cef81c
Merge remote-tracking branch 'origin/APP-440-Create-application-descr…
asafgabai May 20, 2025
b9ec8c9
Create application command
asafgabai May 20, 2025
0b7e0c3
Create application command
asafgabai May 20, 2025
9a54ab8
Merge remote-tracking branch 'origin/APP-440-Create-application-descr…
asafgabai May 20, 2025
9741a02
Update application command
asafgabai May 20, 2025
1d39479
Merge remote-tracking branch 'origin/main' into APP-443-Update-specif…
asafgabai May 20, 2025
89ff137
Update application command
asafgabai May 20, 2025
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
3 changes: 1 addition & 2 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@ linters-settings:
# https://staticcheck.io/docs/options#checks
checks: [ "all","-SA1019","-SA1029" ]
gosec:
excludes: [ "G204", "G301", "G302", "G304", "G306", "G601", "G101", "G407" ]
excludes: [ "G204", "G301", "G302", "G304", "G306", "G601", "G101" ]
exclude-generated: true
exclude-test-files: true
config:
global:
nosec: true
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ prereq::
GOBIN=${TOOLS_DIR} $(GOCMD) install go.uber.org/mock/[email protected]
${TOOLS_DIR}/mockgen --version

build::
build:: clean generate-mock
$(GOCMD) env GOOS GOARCH
$(GOCMD) build -ldflags="${LINKERFLAGS}" -gcflags ${COMPILERFLAGS} -o ${BINARY_CLI}/application-cli-plugin main.go

Expand Down
16 changes: 12 additions & 4 deletions application/app/context.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,36 @@
package app

import (
"github.com/jfrog/jfrog-cli-application/application/service/applications"
"github.com/jfrog/jfrog-cli-application/application/service/systems"
"github.com/jfrog/jfrog-cli-application/application/service/versions"
)

type Context interface {
GetApplicationService() applications.ApplicationService
GetVersionService() versions.VersionService
GetSystemService() systems.SystemService
GetConfig() interface{}
}

type context struct {
versionService versions.VersionService
systemService systems.SystemService
applicationService applications.ApplicationService
versionService versions.VersionService
systemService systems.SystemService
}

func NewAppContext() Context {
return &context{
versionService: versions.NewVersionService(),
systemService: systems.NewSystemService(),
applicationService: applications.NewApplicationService(),
versionService: versions.NewVersionService(),
systemService: systems.NewSystemService(),
}
}

func (c *context) GetApplicationService() applications.ApplicationService {
return c.applicationService
}

func (c *context) GetVersionService() versions.VersionService {
return c.versionService
}
Expand Down
10 changes: 10 additions & 0 deletions application/app/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package app
import (
"testing"

mockapplications "github.com/jfrog/jfrog-cli-application/application/service/applications/mocks"
mocksystems "github.com/jfrog/jfrog-cli-application/application/service/systems/mocks"
mockversions "github.com/jfrog/jfrog-cli-application/application/service/versions/mocks"

Expand All @@ -12,10 +13,19 @@ import (
func TestNewAppContext(t *testing.T) {
ctx := NewAppContext()
assert.NotNil(t, ctx)
assert.NotNil(t, ctx.GetApplicationService())
assert.NotNil(t, ctx.GetVersionService())
assert.NotNil(t, ctx.GetSystemService())
}

func TestGetApplicationService(t *testing.T) {
mockApplicationService := &mockapplications.MockApplicationService{}
ctx := &context{
applicationService: mockApplicationService,
}
assert.Equal(t, mockApplicationService, ctx.GetApplicationService())
}

func TestGetVersionService(t *testing.T) {
mockVersionService := &mockversions.MockVersionService{}
ctx := &context{
Expand Down
3 changes: 3 additions & 0 deletions application/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cli

import (
"github.com/jfrog/jfrog-cli-application/application/app"
"github.com/jfrog/jfrog-cli-application/application/commands/application"
"github.com/jfrog/jfrog-cli-application/application/commands/system"
"github.com/jfrog/jfrog-cli-application/application/commands/version"
"github.com/jfrog/jfrog-cli-core/v2/plugins/components"
Expand Down Expand Up @@ -38,6 +39,8 @@ func GetJfrogApplicationCli() components.App {
system.GetPingCommand(appContext),
version.GetCreateAppVersionCommand(appContext),
version.GetPromoteAppVersionCommand(appContext),
application.GetCreateAppCommand(appContext),
application.GetUpdateAppCommand(appContext),
},
)
return appEntity
Expand Down
132 changes: 132 additions & 0 deletions application/commands/application/create_app_cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package application

import (
pluginsCommon "github.com/jfrog/jfrog-cli-core/v2/plugins/common"

Check failure on line 4 in application/commands/application/create_app_cmd.go

View workflow job for this annotation

GitHub Actions / Static-Check

File is not properly formatted (gofumpt)

Check failure on line 4 in application/commands/application/create_app_cmd.go

View workflow job for this annotation

GitHub Actions / Static-Check

File is not properly formatted (gofumpt)
"slices"

Check failure on line 6 in application/commands/application/create_app_cmd.go

View workflow job for this annotation

GitHub Actions / Static-Check

File is not properly formatted (gofumpt)

Check failure on line 6 in application/commands/application/create_app_cmd.go

View workflow job for this annotation

GitHub Actions / Static-Check

File is not properly formatted (gofumpt)
"github.com/jfrog/jfrog-cli-application/application/commands/utils"
"github.com/jfrog/jfrog-cli-application/application/model"
"github.com/jfrog/jfrog-cli-application/application/service"
commonCLiCommands "github.com/jfrog/jfrog-cli-core/v2/common/commands"
"github.com/jfrog/jfrog-cli-core/v2/plugins/components"
coreConfig "github.com/jfrog/jfrog-cli-core/v2/utils/config"
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
"github.com/jfrog/jfrog-client-go/utils/errorutils"

"github.com/jfrog/jfrog-cli-application/application/app"
"github.com/jfrog/jfrog-cli-application/application/commands"
"github.com/jfrog/jfrog-cli-application/application/common"
"github.com/jfrog/jfrog-cli-application/application/service/applications"
)

type createAppCommand struct {
serverDetails *coreConfig.ServerDetails
applicationService applications.ApplicationService
requestBody *model.AppDescriptor
}

func (cac *createAppCommand) Run() error {
ctx, err := service.NewContext(*cac.serverDetails)
if err != nil {
return err
}

return cac.applicationService.CreateApplication(ctx, cac.requestBody)
}

func (cac *createAppCommand) ServerDetails() (*coreConfig.ServerDetails, error) {
return cac.serverDetails, nil
}

func (cac *createAppCommand) CommandName() string {
return commands.CreateApp
}

func (cac *createAppCommand) buildRequestPayload(ctx *components.Context) (*model.AppDescriptor, error) {
appKey := ctx.Arguments[0]
displayName := ctx.GetStringFlagValue(commands.DisplayNameFlag)
if displayName == "" {
// Default to the application key if display name is not provided
displayName = appKey
}

project := ctx.GetStringFlagValue(commands.ProjectFlag)
if project == "" {
return nil, errorutils.CheckErrorf("--%s is mandatory", commands.ProjectFlag)
}

businessCriticality := ctx.GetStringFlagValue(commands.BusinessCriticalityFlag)
if businessCriticality == "" {
// Default to "unspecified" if not provided
businessCriticality = model.BusinessCriticalityValues[0]
} else if !slices.Contains(model.BusinessCriticalityValues, businessCriticality) {
return nil, errorutils.CheckErrorf("invalid value for --%s: '%s'. Allowed values: %s", commands.BusinessCriticalityFlag, businessCriticality, coreutils.ListToText(model.BusinessCriticalityValues))
}

maturityLevel := ctx.GetStringFlagValue(commands.MaturityLevelFlag)
if maturityLevel == "" {
// Default to "unspecified" if not provided
maturityLevel = model.MaturityLevelValues[0]
} else if !slices.Contains(model.MaturityLevelValues, maturityLevel) {
return nil, errorutils.CheckErrorf("invalid value for --%s: '%s'. Allowed values: %s", commands.MaturityLevelFlag, maturityLevel, coreutils.ListToText(model.MaturityLevelValues))
}

description := ctx.GetStringFlagValue(commands.DescriptionFlag)
userOwners := utils.ParseSliceFlag(ctx.GetStringFlagValue(commands.UserOwnersFlag))
groupOwners := utils.ParseSliceFlag(ctx.GetStringFlagValue(commands.GroupOwnersFlag))
labelsMap, err := utils.ParseMapFlag(ctx.GetStringFlagValue(commands.LabelsFlag))
if err != nil {
return nil, err
}

return &model.AppDescriptor{
ApplicationName: displayName,
ApplicationKey: appKey,
Description: description,
ProjectKey: project,
MaturityLevel: maturityLevel,
BusinessCriticality: businessCriticality,
Labels: labelsMap,
UserOwners: userOwners,
GroupOwners: groupOwners,
}, nil
}

func (cac *createAppCommand) prepareAndRunCommand(ctx *components.Context) error {
if len(ctx.Arguments) != 1 {
return pluginsCommon.WrongNumberOfArgumentsHandler(ctx)
}

var err error
cac.requestBody, err = cac.buildRequestPayload(ctx)
if err != nil {
return err
}

cac.serverDetails, err = utils.ServerDetailsByFlags(ctx)
if err != nil {
return err
}

return commonCLiCommands.Exec(cac)
}

func GetCreateAppCommand(appContext app.Context) components.Command {
cmd := &createAppCommand{
applicationService: appContext.GetApplicationService(),
}
return components.Command{
Name: "create",
Description: "Create a new application",
Category: common.CategoryApplication,
Arguments: []components.Argument{
{
Name: "application-key",
Description: "The key of the application to create",
Optional: false,
},
},
Flags: commands.GetCommandFlags(commands.CreateApp),
Action: cmd.prepareAndRunCommand,
}
}
85 changes: 85 additions & 0 deletions application/commands/application/create_app_cmd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package application

import (
"errors"
"flag"
"github.com/urfave/cli"

Check failure on line 6 in application/commands/application/create_app_cmd_test.go

View workflow job for this annotation

GitHub Actions / Static-Check

File is not properly formatted (gofumpt)

Check failure on line 6 in application/commands/application/create_app_cmd_test.go

View workflow job for this annotation

GitHub Actions / Static-Check

File is not properly formatted (gofumpt)
"testing"

Check failure on line 8 in application/commands/application/create_app_cmd_test.go

View workflow job for this annotation

GitHub Actions / Static-Check

File is not properly formatted (gofumpt)

Check failure on line 8 in application/commands/application/create_app_cmd_test.go

View workflow job for this annotation

GitHub Actions / Static-Check

File is not properly formatted (gofumpt)
"github.com/jfrog/jfrog-cli-application/application/model"
mockapps "github.com/jfrog/jfrog-cli-application/application/service/applications/mocks"
"github.com/jfrog/jfrog-cli-core/v2/plugins/components"
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
)

func TestCreateAppCommand_Run(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

serverDetails := &config.ServerDetails{Url: "https://example.com"}
requestPayload := &model.AppDescriptor{
ApplicationKey: "app-key",
ApplicationName: "app-name",
ProjectKey: "proj-key",
}

mockAppService := mockapps.NewMockApplicationService(ctrl)
mockAppService.EXPECT().CreateApplication(gomock.Any(), requestPayload).Return(nil).Times(1)

cmd := &createAppCommand{
applicationService: mockAppService,
serverDetails: serverDetails,
requestBody: requestPayload,
}

err := cmd.Run()
assert.NoError(t, err)
}

func TestCreateAppCommand_Error(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

serverDetails := &config.ServerDetails{Url: "https://example.com"}
requestPayload := &model.AppDescriptor{
ApplicationKey: "app-key",
ApplicationName: "app-name",
ProjectKey: "proj-key",
}

mockAppService := mockapps.NewMockApplicationService(ctrl)
mockAppService.EXPECT().CreateApplication(gomock.Any(), requestPayload).Return(errors.New("failed to create an application. Status code: 500")).Times(1)

cmd := &createAppCommand{
applicationService: mockAppService,
serverDetails: serverDetails,
requestBody: requestPayload,
}

err := cmd.Run()
assert.Error(t, err)
assert.Equal(t, "failed to create an application. Status code: 500", err.Error())
}

func TestCreateAppCommand_WrongNumberOfArguments(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
app := cli.NewApp()
set := flag.NewFlagSet("test", 0)
ctx := cli.NewContext(app, set, nil)

mockAppService := mockapps.NewMockApplicationService(ctrl)
cmd := &createAppCommand{
applicationService: mockAppService,
}

// Test with no arguments
context, err := components.ConvertContext(ctx)
assert.NoError(t, err)

err = cmd.prepareAndRunCommand(context)
assert.Error(t, err)
assert.Contains(t, err.Error(), "Wrong number of arguments")
}
Loading
Loading