ucn 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ucn might be problematic. Click here for more details.
- package/.claude/skills/ucn/SKILL.md +77 -0
- package/LICENSE +21 -0
- package/README.md +135 -0
- package/cli/index.js +2437 -0
- package/core/discovery.js +513 -0
- package/core/imports.js +558 -0
- package/core/output.js +1274 -0
- package/core/parser.js +279 -0
- package/core/project.js +3261 -0
- package/index.js +52 -0
- package/languages/go.js +653 -0
- package/languages/index.js +267 -0
- package/languages/java.js +826 -0
- package/languages/javascript.js +1346 -0
- package/languages/python.js +667 -0
- package/languages/rust.js +950 -0
- package/languages/utils.js +457 -0
- package/package.json +42 -0
- package/test/fixtures/go/go.mod +3 -0
- package/test/fixtures/go/main.go +257 -0
- package/test/fixtures/go/service.go +187 -0
- package/test/fixtures/java/DataService.java +279 -0
- package/test/fixtures/java/Main.java +287 -0
- package/test/fixtures/java/Utils.java +199 -0
- package/test/fixtures/java/pom.xml +6 -0
- package/test/fixtures/javascript/main.js +109 -0
- package/test/fixtures/javascript/package.json +1 -0
- package/test/fixtures/javascript/service.js +88 -0
- package/test/fixtures/javascript/utils.js +67 -0
- package/test/fixtures/python/main.py +198 -0
- package/test/fixtures/python/pyproject.toml +3 -0
- package/test/fixtures/python/service.py +166 -0
- package/test/fixtures/python/utils.py +118 -0
- package/test/fixtures/rust/Cargo.toml +3 -0
- package/test/fixtures/rust/main.rs +253 -0
- package/test/fixtures/rust/service.rs +210 -0
- package/test/fixtures/rust/utils.rs +154 -0
- package/test/fixtures/typescript/main.ts +154 -0
- package/test/fixtures/typescript/package.json +1 -0
- package/test/fixtures/typescript/repository.ts +149 -0
- package/test/fixtures/typescript/types.ts +114 -0
- package/test/parser.test.js +3661 -0
- package/test/public-repos-test.js +477 -0
- package/test/systematic-test.js +619 -0
- package/ucn.js +8 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
// Package main provides test fixtures for Go parsing.
|
|
2
|
+
package main
|
|
3
|
+
|
|
4
|
+
import (
|
|
5
|
+
"context"
|
|
6
|
+
"errors"
|
|
7
|
+
"fmt"
|
|
8
|
+
"sync"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// Status represents the status of a task.
|
|
12
|
+
type Status string
|
|
13
|
+
|
|
14
|
+
const (
|
|
15
|
+
StatusPending Status = "pending"
|
|
16
|
+
StatusActive Status = "active"
|
|
17
|
+
StatusCompleted Status = "completed"
|
|
18
|
+
StatusFailed Status = "failed"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
// Task represents a task entity.
|
|
22
|
+
type Task struct {
|
|
23
|
+
ID string
|
|
24
|
+
Name string
|
|
25
|
+
Status Status
|
|
26
|
+
Priority int
|
|
27
|
+
Metadata map[string]interface{}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// TaskManager manages tasks.
|
|
31
|
+
type TaskManager struct {
|
|
32
|
+
tasks []*Task
|
|
33
|
+
mu sync.RWMutex
|
|
34
|
+
service *DataService
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// NewTaskManager creates a new task manager.
|
|
38
|
+
func NewTaskManager(service *DataService) *TaskManager {
|
|
39
|
+
return &TaskManager{
|
|
40
|
+
tasks: make([]*Task, 0),
|
|
41
|
+
service: service,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// AddTask adds a task to the manager.
|
|
46
|
+
func (tm *TaskManager) AddTask(task *Task) error {
|
|
47
|
+
if err := ValidateTask(task); err != nil {
|
|
48
|
+
return err
|
|
49
|
+
}
|
|
50
|
+
tm.mu.Lock()
|
|
51
|
+
defer tm.mu.Unlock()
|
|
52
|
+
tm.tasks = append(tm.tasks, task)
|
|
53
|
+
return nil
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// GetTask retrieves a task by ID.
|
|
57
|
+
func (tm *TaskManager) GetTask(id string) (*Task, error) {
|
|
58
|
+
tm.mu.RLock()
|
|
59
|
+
defer tm.mu.RUnlock()
|
|
60
|
+
for _, task := range tm.tasks {
|
|
61
|
+
if task.ID == id {
|
|
62
|
+
return task, nil
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return nil, errors.New("task not found")
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// GetTasks returns all tasks, optionally filtered.
|
|
69
|
+
func (tm *TaskManager) GetTasks(filter func(*Task) bool) []*Task {
|
|
70
|
+
tm.mu.RLock()
|
|
71
|
+
defer tm.mu.RUnlock()
|
|
72
|
+
if filter == nil {
|
|
73
|
+
result := make([]*Task, len(tm.tasks))
|
|
74
|
+
copy(result, tm.tasks)
|
|
75
|
+
return result
|
|
76
|
+
}
|
|
77
|
+
var result []*Task
|
|
78
|
+
for _, task := range tm.tasks {
|
|
79
|
+
if filter(task) {
|
|
80
|
+
result = append(result, task)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return result
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// UpdateTask updates a task by ID.
|
|
87
|
+
func (tm *TaskManager) UpdateTask(id string, updates map[string]interface{}) (*Task, error) {
|
|
88
|
+
tm.mu.Lock()
|
|
89
|
+
defer tm.mu.Unlock()
|
|
90
|
+
for _, task := range tm.tasks {
|
|
91
|
+
if task.ID == id {
|
|
92
|
+
applyUpdates(task, updates)
|
|
93
|
+
return task, nil
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return nil, errors.New("task not found")
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// DeleteTask removes a task by ID.
|
|
100
|
+
func (tm *TaskManager) DeleteTask(id string) bool {
|
|
101
|
+
tm.mu.Lock()
|
|
102
|
+
defer tm.mu.Unlock()
|
|
103
|
+
for i, task := range tm.tasks {
|
|
104
|
+
if task.ID == id {
|
|
105
|
+
tm.tasks = append(tm.tasks[:i], tm.tasks[i+1:]...)
|
|
106
|
+
return true
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return false
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// SyncTasks syncs tasks with the service.
|
|
113
|
+
func (tm *TaskManager) SyncTasks(ctx context.Context) error {
|
|
114
|
+
tm.mu.RLock()
|
|
115
|
+
defer tm.mu.RUnlock()
|
|
116
|
+
for _, task := range tm.tasks {
|
|
117
|
+
if err := tm.service.Save(ctx, task); err != nil {
|
|
118
|
+
return err
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return nil
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ValidateTask validates a task.
|
|
125
|
+
func ValidateTask(task *Task) error {
|
|
126
|
+
if task == nil {
|
|
127
|
+
return errors.New("task cannot be nil")
|
|
128
|
+
}
|
|
129
|
+
if task.ID == "" {
|
|
130
|
+
return errors.New("task ID is required")
|
|
131
|
+
}
|
|
132
|
+
if task.Name == "" {
|
|
133
|
+
return errors.New("task name is required")
|
|
134
|
+
}
|
|
135
|
+
return nil
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// applyUpdates applies updates to a task.
|
|
139
|
+
func applyUpdates(task *Task, updates map[string]interface{}) {
|
|
140
|
+
if name, ok := updates["name"].(string); ok {
|
|
141
|
+
task.Name = name
|
|
142
|
+
}
|
|
143
|
+
if status, ok := updates["status"].(Status); ok {
|
|
144
|
+
task.Status = status
|
|
145
|
+
}
|
|
146
|
+
if priority, ok := updates["priority"].(int); ok {
|
|
147
|
+
task.Priority = priority
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// CreateTask is a factory function for tasks.
|
|
152
|
+
func CreateTask(name string, priority int) *Task {
|
|
153
|
+
return &Task{
|
|
154
|
+
ID: generateID(),
|
|
155
|
+
Name: name,
|
|
156
|
+
Status: StatusPending,
|
|
157
|
+
Priority: priority,
|
|
158
|
+
Metadata: make(map[string]interface{}),
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// generateID generates a unique ID.
|
|
163
|
+
func generateID() string {
|
|
164
|
+
return fmt.Sprintf("task-%d", idCounter)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
var idCounter = 0
|
|
168
|
+
|
|
169
|
+
// FilterByStatus filters tasks by status.
|
|
170
|
+
func FilterByStatus(tasks []*Task, status Status) []*Task {
|
|
171
|
+
var result []*Task
|
|
172
|
+
for _, task := range tasks {
|
|
173
|
+
if task.Status == status {
|
|
174
|
+
result = append(result, task)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return result
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// FilterByPriority filters tasks by minimum priority.
|
|
181
|
+
func FilterByPriority(tasks []*Task, minPriority int) []*Task {
|
|
182
|
+
var result []*Task
|
|
183
|
+
for _, task := range tasks {
|
|
184
|
+
if task.Priority >= minPriority {
|
|
185
|
+
result = append(result, task)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return result
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// TaskProcessor processes tasks.
|
|
192
|
+
type TaskProcessor struct {
|
|
193
|
+
manager *TaskManager
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// NewTaskProcessor creates a new task processor.
|
|
197
|
+
func NewTaskProcessor(manager *TaskManager) *TaskProcessor {
|
|
198
|
+
return &TaskProcessor{manager: manager}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ProcessAll processes all tasks.
|
|
202
|
+
func (tp *TaskProcessor) ProcessAll() ([]map[string]interface{}, error) {
|
|
203
|
+
tasks := tp.manager.GetTasks(nil)
|
|
204
|
+
results := make([]map[string]interface{}, 0, len(tasks))
|
|
205
|
+
for _, task := range tasks {
|
|
206
|
+
result, err := tp.processTask(task)
|
|
207
|
+
if err != nil {
|
|
208
|
+
return nil, err
|
|
209
|
+
}
|
|
210
|
+
results = append(results, result)
|
|
211
|
+
}
|
|
212
|
+
return results, nil
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ProcessPending processes only pending tasks.
|
|
216
|
+
func (tp *TaskProcessor) ProcessPending() ([]map[string]interface{}, error) {
|
|
217
|
+
tasks := tp.manager.GetTasks(func(t *Task) bool {
|
|
218
|
+
return t.Status == StatusPending
|
|
219
|
+
})
|
|
220
|
+
results := make([]map[string]interface{}, 0, len(tasks))
|
|
221
|
+
for _, task := range tasks {
|
|
222
|
+
result, err := tp.processTask(task)
|
|
223
|
+
if err != nil {
|
|
224
|
+
return nil, err
|
|
225
|
+
}
|
|
226
|
+
results = append(results, result)
|
|
227
|
+
}
|
|
228
|
+
return results, nil
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// processTask processes a single task.
|
|
232
|
+
func (tp *TaskProcessor) processTask(task *Task) (map[string]interface{}, error) {
|
|
233
|
+
return FormatTask(task), nil
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// FormatTask formats a task as a map.
|
|
237
|
+
func FormatTask(task *Task) map[string]interface{} {
|
|
238
|
+
return map[string]interface{}{
|
|
239
|
+
"id": task.ID,
|
|
240
|
+
"name": task.Name,
|
|
241
|
+
"status": task.Status,
|
|
242
|
+
"priority": task.Priority,
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// unusedFunction is a function that's never called.
|
|
247
|
+
func unusedFunction() string {
|
|
248
|
+
return "never used"
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
func main() {
|
|
252
|
+
service := NewDataService(nil)
|
|
253
|
+
manager := NewTaskManager(service)
|
|
254
|
+
task := CreateTask("Test Task", 1)
|
|
255
|
+
manager.AddTask(task)
|
|
256
|
+
fmt.Printf("Created task: %s\n", task.Name)
|
|
257
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// Package main provides service implementations.
|
|
2
|
+
package main
|
|
3
|
+
|
|
4
|
+
import (
|
|
5
|
+
"context"
|
|
6
|
+
"errors"
|
|
7
|
+
"sync"
|
|
8
|
+
"time"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// Config holds service configuration.
|
|
12
|
+
type Config struct {
|
|
13
|
+
APIURL string
|
|
14
|
+
Timeout time.Duration
|
|
15
|
+
Retries int
|
|
16
|
+
Debug bool
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// DefaultConfig returns the default configuration.
|
|
20
|
+
func DefaultConfig() *Config {
|
|
21
|
+
return &Config{
|
|
22
|
+
APIURL: "https://api.example.com",
|
|
23
|
+
Timeout: 5 * time.Second,
|
|
24
|
+
Retries: 3,
|
|
25
|
+
Debug: false,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// DataService provides data operations.
|
|
30
|
+
type DataService struct {
|
|
31
|
+
config *Config
|
|
32
|
+
storage map[string]interface{}
|
|
33
|
+
mu sync.RWMutex
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// NewDataService creates a new data service.
|
|
37
|
+
func NewDataService(config *Config) *DataService {
|
|
38
|
+
if config == nil {
|
|
39
|
+
config = DefaultConfig()
|
|
40
|
+
}
|
|
41
|
+
return &DataService{
|
|
42
|
+
config: config,
|
|
43
|
+
storage: make(map[string]interface{}),
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Save saves an entity.
|
|
48
|
+
func (ds *DataService) Save(ctx context.Context, entity interface{}) error {
|
|
49
|
+
id, err := getEntityID(entity)
|
|
50
|
+
if err != nil {
|
|
51
|
+
return err
|
|
52
|
+
}
|
|
53
|
+
ds.mu.Lock()
|
|
54
|
+
defer ds.mu.Unlock()
|
|
55
|
+
ds.storage[id] = entity
|
|
56
|
+
return nil
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Find finds an entity by ID.
|
|
60
|
+
func (ds *DataService) Find(ctx context.Context, id string) (interface{}, error) {
|
|
61
|
+
ds.mu.RLock()
|
|
62
|
+
defer ds.mu.RUnlock()
|
|
63
|
+
if entity, ok := ds.storage[id]; ok {
|
|
64
|
+
return entity, nil
|
|
65
|
+
}
|
|
66
|
+
return nil, errors.New("entity not found")
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// FindAll returns all entities.
|
|
70
|
+
func (ds *DataService) FindAll(ctx context.Context) []interface{} {
|
|
71
|
+
ds.mu.RLock()
|
|
72
|
+
defer ds.mu.RUnlock()
|
|
73
|
+
result := make([]interface{}, 0, len(ds.storage))
|
|
74
|
+
for _, entity := range ds.storage {
|
|
75
|
+
result = append(result, entity)
|
|
76
|
+
}
|
|
77
|
+
return result
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Delete removes an entity.
|
|
81
|
+
func (ds *DataService) Delete(ctx context.Context, id string) error {
|
|
82
|
+
ds.mu.Lock()
|
|
83
|
+
defer ds.mu.Unlock()
|
|
84
|
+
if _, ok := ds.storage[id]; !ok {
|
|
85
|
+
return errors.New("entity not found")
|
|
86
|
+
}
|
|
87
|
+
delete(ds.storage, id)
|
|
88
|
+
return nil
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Clear removes all entities.
|
|
92
|
+
func (ds *DataService) Clear() {
|
|
93
|
+
ds.mu.Lock()
|
|
94
|
+
defer ds.mu.Unlock()
|
|
95
|
+
ds.storage = make(map[string]interface{})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// getEntityID extracts the ID from an entity.
|
|
99
|
+
func getEntityID(entity interface{}) (string, error) {
|
|
100
|
+
if task, ok := entity.(*Task); ok {
|
|
101
|
+
return task.ID, nil
|
|
102
|
+
}
|
|
103
|
+
return "", errors.New("unknown entity type")
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// CacheService provides caching.
|
|
107
|
+
type CacheService struct {
|
|
108
|
+
ttl time.Duration
|
|
109
|
+
cache map[string]interface{}
|
|
110
|
+
timestamps map[string]time.Time
|
|
111
|
+
mu sync.RWMutex
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// NewCacheService creates a new cache service.
|
|
115
|
+
func NewCacheService(ttl time.Duration) *CacheService {
|
|
116
|
+
return &CacheService{
|
|
117
|
+
ttl: ttl,
|
|
118
|
+
cache: make(map[string]interface{}),
|
|
119
|
+
timestamps: make(map[string]time.Time),
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Get retrieves a value from cache.
|
|
124
|
+
func (cs *CacheService) Get(key string) (interface{}, bool) {
|
|
125
|
+
cs.mu.RLock()
|
|
126
|
+
defer cs.mu.RUnlock()
|
|
127
|
+
if value, ok := cs.cache[key]; ok {
|
|
128
|
+
if time.Since(cs.timestamps[key]) < cs.ttl {
|
|
129
|
+
return value, true
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return nil, false
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Set stores a value in cache.
|
|
136
|
+
func (cs *CacheService) Set(key string, value interface{}) {
|
|
137
|
+
cs.mu.Lock()
|
|
138
|
+
defer cs.mu.Unlock()
|
|
139
|
+
cs.cache[key] = value
|
|
140
|
+
cs.timestamps[key] = time.Now()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Delete removes a value from cache.
|
|
144
|
+
func (cs *CacheService) Delete(key string) bool {
|
|
145
|
+
cs.mu.Lock()
|
|
146
|
+
defer cs.mu.Unlock()
|
|
147
|
+
if _, ok := cs.cache[key]; ok {
|
|
148
|
+
delete(cs.cache, key)
|
|
149
|
+
delete(cs.timestamps, key)
|
|
150
|
+
return true
|
|
151
|
+
}
|
|
152
|
+
return false
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Clear removes all values from cache.
|
|
156
|
+
func (cs *CacheService) Clear() {
|
|
157
|
+
cs.mu.Lock()
|
|
158
|
+
defer cs.mu.Unlock()
|
|
159
|
+
cs.cache = make(map[string]interface{})
|
|
160
|
+
cs.timestamps = make(map[string]time.Time)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// CleanupExpired removes expired entries.
|
|
164
|
+
func (cs *CacheService) CleanupExpired() int {
|
|
165
|
+
cs.mu.Lock()
|
|
166
|
+
defer cs.mu.Unlock()
|
|
167
|
+
count := 0
|
|
168
|
+
for key, ts := range cs.timestamps {
|
|
169
|
+
if time.Since(ts) >= cs.ttl {
|
|
170
|
+
delete(cs.cache, key)
|
|
171
|
+
delete(cs.timestamps, key)
|
|
172
|
+
count++
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return count
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Repository defines the repository interface.
|
|
179
|
+
type Repository interface {
|
|
180
|
+
Save(ctx context.Context, entity interface{}) error
|
|
181
|
+
Find(ctx context.Context, id string) (interface{}, error)
|
|
182
|
+
FindAll(ctx context.Context) []interface{}
|
|
183
|
+
Delete(ctx context.Context, id string) error
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Ensure DataService implements Repository.
|
|
187
|
+
var _ Repository = (*DataService)(nil)
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
package fixtures;
|
|
2
|
+
|
|
3
|
+
import java.util.*;
|
|
4
|
+
import java.util.concurrent.ConcurrentHashMap;
|
|
5
|
+
import java.time.Instant;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Data service for managing entities.
|
|
9
|
+
* @param <T> The entity type
|
|
10
|
+
*/
|
|
11
|
+
public class DataService<T> {
|
|
12
|
+
private final Map<String, T> storage;
|
|
13
|
+
private final Config config;
|
|
14
|
+
|
|
15
|
+
public DataService() {
|
|
16
|
+
this(new Config());
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public DataService(Config config) {
|
|
20
|
+
this.storage = new ConcurrentHashMap<>();
|
|
21
|
+
this.config = config;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Save an entity.
|
|
26
|
+
*/
|
|
27
|
+
public void save(T entity) {
|
|
28
|
+
String id = getEntityId(entity);
|
|
29
|
+
storage.put(id, entity);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Find an entity by ID.
|
|
34
|
+
*/
|
|
35
|
+
public Optional<T> find(String id) {
|
|
36
|
+
return Optional.ofNullable(storage.get(id));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Find all entities.
|
|
41
|
+
*/
|
|
42
|
+
public List<T> findAll() {
|
|
43
|
+
return new ArrayList<>(storage.values());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Delete an entity by ID.
|
|
48
|
+
*/
|
|
49
|
+
public boolean delete(String id) {
|
|
50
|
+
return storage.remove(id) != null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Clear all entities.
|
|
55
|
+
*/
|
|
56
|
+
public void clear() {
|
|
57
|
+
storage.clear();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get the config.
|
|
62
|
+
*/
|
|
63
|
+
public Config getConfig() {
|
|
64
|
+
return config;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get the entity ID using reflection.
|
|
69
|
+
*/
|
|
70
|
+
@SuppressWarnings("unchecked")
|
|
71
|
+
private String getEntityId(T entity) {
|
|
72
|
+
try {
|
|
73
|
+
var method = entity.getClass().getMethod("getId");
|
|
74
|
+
return (String) method.invoke(entity);
|
|
75
|
+
} catch (Exception e) {
|
|
76
|
+
throw new RuntimeException("Entity must have getId() method", e);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Configuration class.
|
|
83
|
+
*/
|
|
84
|
+
class Config {
|
|
85
|
+
private String apiUrl;
|
|
86
|
+
private int timeout;
|
|
87
|
+
private int retries;
|
|
88
|
+
private boolean debug;
|
|
89
|
+
|
|
90
|
+
public Config() {
|
|
91
|
+
this.apiUrl = "https://api.example.com";
|
|
92
|
+
this.timeout = 5000;
|
|
93
|
+
this.retries = 3;
|
|
94
|
+
this.debug = false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public String getApiUrl() {
|
|
98
|
+
return apiUrl;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public void setApiUrl(String apiUrl) {
|
|
102
|
+
this.apiUrl = apiUrl;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public int getTimeout() {
|
|
106
|
+
return timeout;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public void setTimeout(int timeout) {
|
|
110
|
+
this.timeout = timeout;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public int getRetries() {
|
|
114
|
+
return retries;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public void setRetries(int retries) {
|
|
118
|
+
this.retries = retries;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
public boolean isDebug() {
|
|
122
|
+
return debug;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public void setDebug(boolean debug) {
|
|
126
|
+
this.debug = debug;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Repository interface.
|
|
132
|
+
* @param <T> The entity type
|
|
133
|
+
*/
|
|
134
|
+
interface Repository<T> {
|
|
135
|
+
void save(T entity);
|
|
136
|
+
Optional<T> find(String id);
|
|
137
|
+
List<T> findAll();
|
|
138
|
+
boolean delete(String id);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Cache service with TTL.
|
|
143
|
+
* @param <T> The cached value type
|
|
144
|
+
*/
|
|
145
|
+
class CacheService<T> {
|
|
146
|
+
private final Map<String, CacheEntry<T>> cache;
|
|
147
|
+
private final long ttlMillis;
|
|
148
|
+
|
|
149
|
+
public CacheService(long ttlMillis) {
|
|
150
|
+
this.cache = new ConcurrentHashMap<>();
|
|
151
|
+
this.ttlMillis = ttlMillis;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get a value from cache.
|
|
156
|
+
*/
|
|
157
|
+
public Optional<T> get(String key) {
|
|
158
|
+
CacheEntry<T> entry = cache.get(key);
|
|
159
|
+
if (entry != null && !entry.isExpired(ttlMillis)) {
|
|
160
|
+
return Optional.of(entry.getValue());
|
|
161
|
+
}
|
|
162
|
+
return Optional.empty();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Set a value in cache.
|
|
167
|
+
*/
|
|
168
|
+
public void set(String key, T value) {
|
|
169
|
+
cache.put(key, new CacheEntry<>(value));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Delete a value from cache.
|
|
174
|
+
*/
|
|
175
|
+
public boolean delete(String key) {
|
|
176
|
+
return cache.remove(key) != null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Clear all values from cache.
|
|
181
|
+
*/
|
|
182
|
+
public void clear() {
|
|
183
|
+
cache.clear();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Remove expired entries.
|
|
188
|
+
*/
|
|
189
|
+
public int cleanupExpired() {
|
|
190
|
+
List<String> expired = new ArrayList<>();
|
|
191
|
+
for (Map.Entry<String, CacheEntry<T>> entry : cache.entrySet()) {
|
|
192
|
+
if (entry.getValue().isExpired(ttlMillis)) {
|
|
193
|
+
expired.add(entry.getKey());
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
for (String key : expired) {
|
|
197
|
+
cache.remove(key);
|
|
198
|
+
}
|
|
199
|
+
return expired.size();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Cache entry with timestamp.
|
|
204
|
+
*/
|
|
205
|
+
private static class CacheEntry<T> {
|
|
206
|
+
private final T value;
|
|
207
|
+
private final Instant timestamp;
|
|
208
|
+
|
|
209
|
+
CacheEntry(T value) {
|
|
210
|
+
this.value = value;
|
|
211
|
+
this.timestamp = Instant.now();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
T getValue() {
|
|
215
|
+
return value;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
boolean isExpired(long ttlMillis) {
|
|
219
|
+
return Instant.now().toEpochMilli() - timestamp.toEpochMilli() >= ttlMillis;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* API client for HTTP requests.
|
|
226
|
+
*/
|
|
227
|
+
class ApiClient {
|
|
228
|
+
private final Config config;
|
|
229
|
+
|
|
230
|
+
public ApiClient(Config config) {
|
|
231
|
+
this.config = config;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Make a GET request.
|
|
236
|
+
*/
|
|
237
|
+
public Map<String, Object> get(String path) {
|
|
238
|
+
String url = buildUrl(path);
|
|
239
|
+
return request("GET", url, null);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Make a POST request.
|
|
244
|
+
*/
|
|
245
|
+
public Map<String, Object> post(String path, Map<String, Object> data) {
|
|
246
|
+
String url = buildUrl(path);
|
|
247
|
+
return request("POST", url, data);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Make a DELETE request.
|
|
252
|
+
*/
|
|
253
|
+
public Map<String, Object> delete(String path) {
|
|
254
|
+
String url = buildUrl(path);
|
|
255
|
+
return request("DELETE", url, null);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Build the full URL.
|
|
260
|
+
*/
|
|
261
|
+
private String buildUrl(String path) {
|
|
262
|
+
if (path.startsWith("http")) {
|
|
263
|
+
return path;
|
|
264
|
+
}
|
|
265
|
+
return config.getApiUrl() + path;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Make an HTTP request.
|
|
270
|
+
*/
|
|
271
|
+
private Map<String, Object> request(String method, String url, Map<String, Object> data) {
|
|
272
|
+
// Simulated request
|
|
273
|
+
Map<String, Object> result = new HashMap<>();
|
|
274
|
+
result.put("status", 200);
|
|
275
|
+
result.put("method", method);
|
|
276
|
+
result.put("url", url);
|
|
277
|
+
return result;
|
|
278
|
+
}
|
|
279
|
+
}
|