Skip to content

Commit 1d26395

Browse files
Merge pull request #124 from kuudori/HYPERFLEET-995
HYPERFLEET-995 - refactor: move logic from handlers to service
2 parents d2c0ca9 + 8dc77ea commit 1d26395

34 files changed

Lines changed: 1234 additions & 790 deletions

pkg/api/adapter_status_types.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,13 @@ func (l AdapterStatusList) Index() AdapterStatusIndex {
4646
}
4747

4848
func (as *AdapterStatus) BeforeCreate(tx *gorm.DB) error {
49-
id, err := NewID()
50-
if err != nil {
51-
return fmt.Errorf("failed to generate adapter status ID: %w", err)
49+
if as.ID == "" {
50+
id, err := NewID()
51+
if err != nil {
52+
return fmt.Errorf("failed to generate adapter status ID: %w", err)
53+
}
54+
as.ID = id
5255
}
53-
as.ID = id
5456
return nil
5557
}
5658

pkg/api/cluster_types.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ type Cluster struct {
2424
Generation int32 `json:"generation" gorm:"default:1;not null"`
2525
}
2626

27-
type ClusterList []*Cluster
28-
type ClusterIndex map[string]*Cluster
27+
type (
28+
ClusterList []*Cluster
29+
ClusterIndex map[string]*Cluster
30+
)
2931

3032
func (l ClusterList) Index() ClusterIndex {
3133
index := ClusterIndex{}
@@ -36,14 +38,18 @@ func (l ClusterList) Index() ClusterIndex {
3638
}
3739

3840
func (c *Cluster) BeforeCreate(tx *gorm.DB) error {
39-
id, err := NewID()
40-
if err != nil {
41-
return fmt.Errorf("failed to generate cluster ID: %w", err)
41+
if c.ID == "" {
42+
id, err := NewID()
43+
if err != nil {
44+
return fmt.Errorf("failed to generate cluster ID: %w", err)
45+
}
46+
c.ID = id
4247
}
43-
c.ID = id
4448

4549
now := time.Now()
46-
c.CreatedTime = now
50+
if c.CreatedTime.IsZero() {
51+
c.CreatedTime = now
52+
}
4753
c.UpdatedTime = now
4854
if c.Generation == 0 {
4955
c.Generation = 1
@@ -64,3 +70,12 @@ type ClusterPatchRequest struct {
6470
Spec *map[string]interface{} `json:"spec,omitempty"`
6571
Labels *map[string]string `json:"labels,omitempty"`
6672
}
73+
74+
func (c *Cluster) MarkDeleted(by string, t time.Time) {
75+
c.DeletedTime = &t
76+
c.DeletedBy = &by
77+
}
78+
79+
func (c *Cluster) IncrementGeneration() {
80+
c.Generation++
81+
}

pkg/api/node_pool_types.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ type NodePool struct {
2828
Generation int32 `json:"generation" gorm:"default:1;not null"`
2929
}
3030

31-
type NodePoolList []*NodePool
32-
type NodePoolIndex map[string]*NodePool
31+
type (
32+
NodePoolList []*NodePool
33+
NodePoolIndex map[string]*NodePool
34+
)
3335

3436
func (l NodePoolList) Index() NodePoolIndex {
3537
index := NodePoolIndex{}
@@ -40,14 +42,18 @@ func (l NodePoolList) Index() NodePoolIndex {
4042
}
4143

4244
func (np *NodePool) BeforeCreate(tx *gorm.DB) error {
43-
id, err := NewID()
44-
if err != nil {
45-
return fmt.Errorf("failed to generate node pool ID: %w", err)
45+
if np.ID == "" {
46+
id, err := NewID()
47+
if err != nil {
48+
return fmt.Errorf("failed to generate node pool ID: %w", err)
49+
}
50+
np.ID = id
4651
}
47-
np.ID = id
4852

4953
now := time.Now()
50-
np.CreatedTime = now
54+
if np.CreatedTime.IsZero() {
55+
np.CreatedTime = now
56+
}
5157
np.UpdatedTime = now
5258
if np.Generation == 0 {
5359
np.Generation = 1
@@ -75,3 +81,12 @@ type NodePoolPatchRequest struct {
7581
Spec *map[string]interface{} `json:"spec,omitempty"`
7682
Labels *map[string]string `json:"labels,omitempty"`
7783
}
84+
85+
func (np *NodePool) MarkDeleted(by string, t time.Time) {
86+
np.DeletedTime = &t
87+
np.DeletedBy = &by
88+
}
89+
90+
func (np *NodePool) IncrementGeneration() {
91+
np.Generation++
92+
}

pkg/api/presenters/cluster.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
)
1212

1313
// ConvertCluster converts openapi.ClusterCreateRequest to api.Cluster (GORM model)
14-
func ConvertCluster(req *openapi.ClusterCreateRequest, createdBy string) (*api.Cluster, error) {
14+
func ConvertCluster(req *openapi.ClusterCreateRequest) (*api.Cluster, error) {
1515
// Marshal Spec
1616
specJSON, err := json.Marshal(req.Spec)
1717
if err != nil {
@@ -40,8 +40,6 @@ func ConvertCluster(req *openapi.ClusterCreateRequest, createdBy string) (*api.C
4040
Spec: specJSON,
4141
Labels: labelsJSON,
4242
Generation: 1,
43-
CreatedBy: createdBy,
44-
UpdatedBy: createdBy,
4543
}, nil
4644
}
4745

pkg/api/presenters/cluster_test.go

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,13 @@ func TestConvertCluster_Complete(t *testing.T) {
3636
RegisterTestingT(t)
3737

3838
req := createTestClusterRequest()
39-
createdBy := "user123"
4039

41-
result, err := ConvertCluster(req, createdBy)
40+
result, err := ConvertCluster(req)
4241
Expect(err).To(BeNil())
4342

4443
// Verify basic fields
4544
Expect(result.Kind).To(Equal("Cluster"))
4645
Expect(result.Name).To(Equal("test-cluster"))
47-
Expect(result.CreatedBy).To(Equal(createdBy))
48-
Expect(result.UpdatedBy).To(Equal(createdBy))
4946

5047
// Verify defaults
5148
Expect(result.Generation).To(Equal(int32(1)))
@@ -83,7 +80,7 @@ func TestConvertCluster_WithLabels(t *testing.T) {
8380
Spec: map[string]interface{}{"test": "spec"},
8481
}
8582

86-
result, err := ConvertCluster(req, "user456")
83+
result, err := ConvertCluster(req)
8784
Expect(err).To(BeNil())
8885

8986
var resultLabels map[string]string
@@ -104,7 +101,7 @@ func TestConvertCluster_WithoutLabels(t *testing.T) {
104101
Spec: map[string]interface{}{"test": "spec"},
105102
}
106103

107-
result, err := ConvertCluster(req, "user789")
104+
result, err := ConvertCluster(req)
108105
Expect(err).To(BeNil())
109106

110107
var resultLabels map[string]string
@@ -135,7 +132,7 @@ func TestConvertCluster_SpecMarshaling(t *testing.T) {
135132
Spec: complexSpec,
136133
}
137134

138-
result, err := ConvertCluster(req, "user000")
135+
result, err := ConvertCluster(req)
139136
Expect(err).To(BeNil())
140137

141138
var resultSpec map[string]interface{}
@@ -319,13 +316,12 @@ func TestConvertAndPresentCluster_RoundTrip(t *testing.T) {
319316
RegisterTestingT(t)
320317

321318
originalReq := createTestClusterRequest()
322-
createdBy := "user999@example.com"
323319

324320
// Convert from OpenAPI request to domain
325-
cluster, err := ConvertCluster(originalReq, createdBy)
321+
cluster, err := ConvertCluster(originalReq)
326322
Expect(err).To(BeNil())
327323

328-
// Simulate database fields (ID, timestamps)
324+
// Simulate database fields (ID, timestamps, created_by/updated_by set by service layer)
329325
cluster.ID = "cluster-roundtrip-123"
330326
now := time.Now()
331327
cluster.CreatedTime = now
@@ -339,8 +335,6 @@ func TestConvertAndPresentCluster_RoundTrip(t *testing.T) {
339335
Expect(*result.Id).To(Equal("cluster-roundtrip-123"))
340336
Expect(result.Kind).To(Equal(originalReq.Kind))
341337
Expect(result.Name).To(Equal(originalReq.Name))
342-
Expect(result.CreatedBy).To(Equal(openapi_types.Email(createdBy)))
343-
Expect(result.UpdatedBy).To(Equal(openapi_types.Email(createdBy)))
344338

345339
// Verify Spec preserved
346340
Expect(result.Spec["region"]).To(Equal(originalReq.Spec["region"]))

pkg/api/presenters/node_pool.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
)
1010

1111
// ConvertNodePool converts openapi.NodePoolCreateRequest to api.NodePool (GORM model)
12-
func ConvertNodePool(req *openapi.NodePoolCreateRequest, ownerID, createdBy string) (*api.NodePool, error) {
12+
func ConvertNodePool(req *openapi.NodePoolCreateRequest, ownerID string) (*api.NodePool, error) {
1313
// Marshal Spec
1414
specJSON, err := json.Marshal(req.Spec)
1515
if err != nil {
@@ -38,8 +38,6 @@ func ConvertNodePool(req *openapi.NodePoolCreateRequest, ownerID, createdBy stri
3838
Labels: labelsJSON,
3939
OwnerID: ownerID,
4040
OwnerKind: "Cluster",
41-
CreatedBy: createdBy,
42-
UpdatedBy: createdBy,
4341
}, nil
4442
}
4543

pkg/api/presenters/node_pool_test.go

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,15 @@ func TestConvertNodePool_Complete(t *testing.T) {
3333

3434
req := createTestNodePoolRequest()
3535
ownerID := "cluster-owner-123"
36-
createdBy := "user456"
3736

38-
result, err := ConvertNodePool(req, ownerID, createdBy)
37+
result, err := ConvertNodePool(req, ownerID)
3938
Expect(err).To(BeNil())
4039

4140
// Verify basic fields
4241
Expect(result.Kind).To(Equal("NodePool"))
4342
Expect(result.Name).To(Equal("test-nodepool"))
4443
Expect(result.OwnerID).To(Equal("cluster-owner-123"))
4544
Expect(result.OwnerKind).To(Equal("Cluster"))
46-
Expect(result.CreatedBy).To(Equal("user456"))
47-
Expect(result.UpdatedBy).To(Equal("user456"))
4845

4946
// Verify Spec marshaled correctly
5047
var spec map[string]interface{}
@@ -75,7 +72,7 @@ func TestConvertNodePool_WithKind(t *testing.T) {
7572
Labels: nil,
7673
}
7774

78-
result, err := ConvertNodePool(req, "cluster-123", "user789")
75+
result, err := ConvertNodePool(req, "cluster-123")
7976
Expect(err).To(BeNil())
8077

8178
Expect(result.Kind).To(Equal("CustomNodePool"))
@@ -92,7 +89,7 @@ func TestConvertNodePool_WithoutKind(t *testing.T) {
9289
Labels: nil,
9390
}
9491

95-
result, err := ConvertNodePool(req, "cluster-456", "user000")
92+
result, err := ConvertNodePool(req, "cluster-456")
9693
Expect(err).To(BeNil())
9794

9895
Expect(result.Kind).To(Equal("NodePool")) // Default value
@@ -114,7 +111,7 @@ func TestConvertNodePool_WithLabels(t *testing.T) {
114111
Labels: &labels,
115112
}
116113

117-
result, err := ConvertNodePool(req, "cluster-789", "user111")
114+
result, err := ConvertNodePool(req, "cluster-789")
118115
Expect(err).To(BeNil())
119116

120117
var resultLabels map[string]string
@@ -135,7 +132,7 @@ func TestConvertNodePool_WithoutLabels(t *testing.T) {
135132
Labels: nil, // Nil labels
136133
}
137134

138-
result, err := ConvertNodePool(req, "cluster-xyz", "user222")
135+
result, err := ConvertNodePool(req, "cluster-xyz")
139136
Expect(err).To(BeNil())
140137

141138
var resultLabels map[string]string
@@ -361,10 +358,9 @@ func TestConvertAndPresentNodePool_RoundTrip(t *testing.T) {
361358

362359
originalReq := createTestNodePoolRequest()
363360
ownerID := "cluster-roundtrip-789"
364-
createdBy := "user-roundtrip@example.com"
365361

366362
// Convert from OpenAPI request to domain
367-
nodePool, err := ConvertNodePool(originalReq, ownerID, createdBy)
363+
nodePool, err := ConvertNodePool(originalReq, ownerID)
368364
Expect(err).To(BeNil())
369365

370366
// Simulate database fields (ID, timestamps)
@@ -381,8 +377,6 @@ func TestConvertAndPresentNodePool_RoundTrip(t *testing.T) {
381377
Expect(*result.Id).To(Equal("nodepool-roundtrip-123"))
382378
Expect(*result.Kind).To(Equal(*originalReq.Kind))
383379
Expect(result.Name).To(Equal(originalReq.Name))
384-
Expect(result.CreatedBy).To(Equal(openapi_types.Email(createdBy)))
385-
Expect(result.UpdatedBy).To(Equal(openapi_types.Email(createdBy)))
386380

387381
// Verify Spec preserved
388382
Expect(result.Spec["replicas"]).To(BeNumerically("==", originalReq.Spec["replicas"]))

pkg/dao/CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ This is critical — without it, the middleware will commit a partially-failed t
3030

3131
## Patterns
3232

33-
- `Replace()` compares spec bytes to detect changes; auto-increments `Generation` only when spec actually changed
33+
- Generation increment is handled by the service layer's `Patch` method via `IncrementGeneration()`; `Save()` persists the result
3434
- Use `clause.Associations` carefully — omit on updates to prevent cascading deletes of related records
3535
- All methods accept `context.Context` as first parameter for transaction propagation
3636
- Return stdlib `error` (not `*errors.ServiceError`) — service layer wraps DAO errors into ServiceErrors

0 commit comments

Comments
 (0)