edge-connect-client/internal/application/instance/service.go
Stephan Lo f1ee439c61 refactor: structure core logic by application use cases
Restructures the internal business logic from a generic `services` package to a use-case-driven design under `internal/application`.

Each primary function of the application (`app`, `instance`, `cloudlet`, `apply`) now resides in its own package. This clarifies the architecture and makes it easier to navigate and extend.

- Moved service implementations to `internal/application/<usecase>/`.
- Kept ports and domain models in `internal/core/`.
- Updated `main.go` and CLI adapters to reflect the new paths.
- Added missing `RefreshAppInstance` method to satisfy the service interface.
- Verified the change with a full build and test run.
2025-10-09 00:00:51 +02:00

185 lines
6.1 KiB
Go

package instance
import (
"context"
"strings"
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/domain"
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/ports/driven"
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/ports/driving"
)
type service struct {
appInstanceRepo driven.AppInstanceRepository
}
func NewService(appInstanceRepo driven.AppInstanceRepository) driving.AppInstanceService {
return &service{appInstanceRepo: appInstanceRepo}
}
func (s *service) CreateAppInstance(ctx context.Context, region string, appInst *domain.AppInstance) error {
if err := s.validateAppInstance(appInst); err != nil {
return err
}
if region == "" {
return domain.ErrMissingRegion
}
if err := s.appInstanceRepo.CreateAppInstance(ctx, region, appInst); err != nil {
if domain.IsNotFoundError(err) {
return domain.NewInstanceError(domain.ErrResourceConflict, "CreateAppInstance", appInst.Key, region,
"app instance may already exist or have conflicting configuration")
}
return domain.NewInstanceError(domain.ErrInternalError, "CreateAppInstance", appInst.Key, region,
"failed to create app instance").WithDetails(err.Error())
}
return nil
}
func (s *service) ShowAppInstance(ctx context.Context, region string, appInstKey domain.AppInstanceKey) (*domain.AppInstance, error) {
if err := s.validateAppInstanceKey(appInstKey); err != nil {
return nil, err
}
if region == "" {
return nil, domain.ErrMissingRegion
}
instance, err := s.appInstanceRepo.ShowAppInstance(ctx, region, appInstKey)
if err != nil {
if domain.IsNotFoundError(err) {
return nil, domain.NewInstanceError(domain.ErrResourceNotFound, "ShowAppInstance", appInstKey, region,
"app instance does not exist")
}
return nil, domain.NewInstanceError(domain.ErrInternalError, "ShowAppInstance", appInstKey, region,
"failed to retrieve app instance").WithDetails(err.Error())
}
return instance, nil
}
func (s *service) ShowAppInstances(ctx context.Context, region string, appInstKey domain.AppInstanceKey) ([]domain.AppInstance, error) {
if region == "" {
return nil, domain.ErrMissingRegion
}
instances, err := s.appInstanceRepo.ShowAppInstances(ctx, region, appInstKey)
if err != nil {
return nil, domain.NewInstanceError(domain.ErrInternalError, "ShowAppInstances", appInstKey, region,
"failed to list app instances").WithDetails(err.Error())
}
return instances, nil
}
func (s *service) DeleteAppInstance(ctx context.Context, region string, appInstKey domain.AppInstanceKey) error {
if err := s.validateAppInstanceKey(appInstKey); err != nil {
return err
}
if region == "" {
return domain.ErrMissingRegion
}
if err := s.appInstanceRepo.DeleteAppInstance(ctx, region, appInstKey); err != nil {
if domain.IsNotFoundError(err) {
return domain.NewInstanceError(domain.ErrResourceNotFound, "DeleteAppInstance", appInstKey, region,
"app instance does not exist")
}
return domain.NewInstanceError(domain.ErrInternalError, "DeleteAppInstance", appInstKey, region,
"failed to delete app instance").WithDetails(err.Error())
}
return nil
}
func (s *service) UpdateAppInstance(ctx context.Context, region string, appInst *domain.AppInstance) error {
if err := s.validateAppInstance(appInst); err != nil {
return err
}
if region == "" {
return domain.ErrMissingRegion
}
if err := s.appInstanceRepo.UpdateAppInstance(ctx, region, appInst); err != nil {
if domain.IsNotFoundError(err) {
return domain.NewInstanceError(domain.ErrResourceConflict, "UpdateAppInstance", appInst.Key, region,
"app instance may already exist or have conflicting configuration")
}
return domain.NewInstanceError(domain.ErrInternalError, "UpdateAppInstance", appInst.Key, region,
"failed to update app instance").WithDetails(err.Error())
}
return nil
}
func (s *service) RefreshAppInstance(ctx context.Context, region string, appInstKey domain.AppInstanceKey) error {
if err := s.validateAppInstanceKey(appInstKey); err != nil {
return err
}
if region == "" {
return domain.ErrMissingRegion
}
// Note: The driven port (repository) does not currently have a Refresh method.
// This is a placeholder implementation.
// To fully implement this, we would need to add RefreshAppInstance to the AppInstanceRepository interface
// and implement it in the edgeconnect adapter.
// For now, we can just return nil or a 'not implemented' error.
// Let's delegate to the Show method as a temporary measure.
_, err := s.appInstanceRepo.ShowAppInstance(ctx, region, appInstKey)
if err != nil {
if domain.IsNotFoundError(err) {
return domain.NewInstanceError(domain.ErrResourceNotFound, "RefreshAppInstance", appInstKey, region,
"app instance does not exist")
}
return domain.NewInstanceError(domain.ErrInternalError, "RefreshAppInstance", appInstKey, region,
"failed to refresh app instance").WithDetails(err.Error())
}
return nil
}
// validateAppInstance performs business logic validation on an app instance
func (s *service) validateAppInstance(appInst *domain.AppInstance) error {
if appInst == nil {
return domain.NewDomainError(domain.ErrValidationFailed, "app instance cannot be nil")
}
if err := s.validateAppInstanceKey(appInst.Key); err != nil {
return err
}
// Validate flavor if present
if strings.TrimSpace(appInst.Flavor.Name) == "" {
return domain.NewDomainError(domain.ErrValidationFailed, "flavor name is required")
}
return nil
}
// validateAppInstanceKey performs business logic validation on an app instance key
func (s *service) validateAppInstanceKey(appInstKey domain.AppInstanceKey) error {
if strings.TrimSpace(appInstKey.Organization) == "" {
return domain.ErrInvalidInstanceKey.WithDetails("organization is required")
}
if strings.TrimSpace(appInstKey.Name) == "" {
return domain.ErrInvalidInstanceKey.WithDetails("name is required")
}
// Validate embedded cloudlet key
if strings.TrimSpace(appInstKey.CloudletKey.Organization) == "" {
return domain.ErrInvalidInstanceKey.WithDetails("cloudlet organization is required")
}
if strings.TrimSpace(appInstKey.CloudletKey.Name) == "" {
return domain.ErrInvalidInstanceKey.WithDetails("cloudlet name is required")
}
return nil
}