package training import ( "sync" "time" "github.com/shopspring/decimal" "gitlab.mareshq.com/hq/yggdrasil/internal/currency" ) type TrainingRepository interface { Create(training *Training) error FindByID(id TrainingID) (*Training, error) FindAll() ([]Training, error) Update(training *Training) error Delete(id TrainingID) error } type InMemoryTrainingRepository struct { trainings map[TrainingID]Training lock sync.RWMutex } func NewInMemoryTrainingRepository() *InMemoryTrainingRepository { repo := &InMemoryTrainingRepository{ trainings: make(map[TrainingID]Training), } _ = repo.Create(&Training{ Name: "Kubernetes", Days: 2, Description: "Kubernetes", Pricing: []TrainingPrice{ { Amount: decimal.NewFromInt(450), Currency: currency.USD, Type: OpenTrainingPrice, }, { Amount: decimal.NewFromInt(2000), Currency: currency.USD, Type: CorporateTrainingPrice, }, { Amount: decimal.NewFromInt(9900), Currency: currency.CZK, Type: OpenTrainingPrice, }, { Amount: decimal.NewFromInt(49000), Currency: currency.CZK, Type: CorporateTrainingPrice, }, }, }) _ = repo.Create(&Training{ Name: "Terraform", Days: 1, Description: "Terraform", Pricing: []TrainingPrice{ { Amount: decimal.NewFromInt(200), Currency: currency.USD, Type: OpenTrainingPrice, }, { Amount: decimal.NewFromInt(1000), Currency: currency.USD, Type: CorporateTrainingPrice, }, { Amount: decimal.NewFromInt(4900), Currency: currency.CZK, Type: OpenTrainingPrice, }, { Amount: decimal.NewFromInt(24000), Currency: currency.CZK, Type: CorporateTrainingPrice, }, }, }) _ = repo.Create(&Training{ Name: "RKE2", Days: 1, Description: "RKE2", Pricing: []TrainingPrice{ { Amount: decimal.NewFromInt(200), Currency: currency.USD, Type: OpenTrainingPrice, }, { Amount: decimal.NewFromInt(1000), Currency: currency.USD, Type: CorporateTrainingPrice, }, { Amount: decimal.NewFromInt(4900), Currency: currency.CZK, Type: OpenTrainingPrice, }, { Amount: decimal.NewFromInt(24000), Currency: currency.CZK, Type: CorporateTrainingPrice, }, }, }) _ = repo.Create(&Training{ Name: "GitHub Actions", Days: 1, Description: "GitHub Actions", Pricing: []TrainingPrice{ { Amount: decimal.NewFromInt(200), Currency: currency.USD, Type: OpenTrainingPrice, }, { Amount: decimal.NewFromInt(1000), Currency: currency.USD, Type: CorporateTrainingPrice, }, { Amount: decimal.NewFromInt(4900), Currency: currency.CZK, Type: OpenTrainingPrice, }, { Amount: decimal.NewFromInt(24000), Currency: currency.CZK, Type: CorporateTrainingPrice, }, }, }) _ = repo.Create(&Training{ Name: "GitLab CI", Days: 1, Description: "GitLab CI", Pricing: []TrainingPrice{ { Amount: decimal.NewFromInt(200), Currency: currency.USD, Type: OpenTrainingPrice, }, { Amount: decimal.NewFromInt(1000), Currency: currency.USD, Type: CorporateTrainingPrice, }, { Amount: decimal.NewFromInt(4900), Currency: currency.CZK, Type: OpenTrainingPrice, }, { Amount: decimal.NewFromInt(24000), Currency: currency.CZK, Type: CorporateTrainingPrice, }, }, }) _ = repo.Create(&Training{ Name: "Prometheus", Days: 2, Description: "Prometheus", Pricing: []TrainingPrice{ { Amount: decimal.NewFromInt(450), Currency: currency.USD, Type: OpenTrainingPrice, }, { Amount: decimal.NewFromInt(2000), Currency: currency.USD, Type: CorporateTrainingPrice, }, { Amount: decimal.NewFromInt(9900), Currency: currency.CZK, Type: OpenTrainingPrice, }, { Amount: decimal.NewFromInt(49000), Currency: currency.CZK, Type: CorporateTrainingPrice, }, }, }) return repo } func (r *InMemoryTrainingRepository) Create(training *Training) error { r.lock.Lock() defer r.lock.Unlock() training.ID = NewTrainingID() r.trainings[training.ID] = *training return nil } func (r *InMemoryTrainingRepository) FindByID(id TrainingID) (*Training, error) { r.lock.RLock() defer r.lock.RUnlock() training, ok := r.trainings[id] if !ok { return nil, ErrTrainingNotFound } return &training, nil } func (r *InMemoryTrainingRepository) FindAll() ([]Training, error) { r.lock.RLock() defer r.lock.RUnlock() trainings := make([]Training, 0, len(r.trainings)) for _, training := range r.trainings { trainings = append(trainings, training) } return trainings, nil } func (r *InMemoryTrainingRepository) Update(training *Training) error { r.lock.Lock() defer r.lock.Unlock() r.trainings[training.ID] = *training return nil } func (r *InMemoryTrainingRepository) Delete(id TrainingID) error { r.lock.Lock() defer r.lock.Unlock() _, ok := r.trainings[id] if !ok { return ErrTrainingNotFound } delete(r.trainings, id) return nil } type TrainingDateRepository interface { Create(trainingID TrainingID, trainingDate *TrainingDate) error FindByID(id TrainingDateID) (*TrainingDate, error) FindAll() ([]TrainingDate, error) FindAllByTrainingID(trainingID TrainingID) ([]TrainingDate, error) FindUpcomingByTrainingID(trainingID TrainingID) ([]TrainingDate, error) FindAllUpcoming() ([]TrainingDate, error) Update(trainingDate *TrainingDate) error Delete(id TrainingDateID) error } type InMemoryTrainingDateRepository struct { trainingDates map[TrainingDateID]TrainingDate trainingToDates map[TrainingID][]TrainingDateID lock sync.RWMutex } func NewInMemoryTrainingDateRepository() *InMemoryTrainingDateRepository { return &InMemoryTrainingDateRepository{ trainingDates: make(map[TrainingDateID]TrainingDate), trainingToDates: make(map[TrainingID][]TrainingDateID), } } func (r *InMemoryTrainingDateRepository) Create(trainingID TrainingID, trainingDate *TrainingDate) error { r.lock.Lock() defer r.lock.Unlock() trainingDate.ID = NewTrainingDateID() trainingDate.trainingID = trainingID r.trainingDates[trainingDate.ID] = *trainingDate r.trainingToDates[trainingID] = append(r.trainingToDates[trainingID], trainingDate.ID) return nil } func (r *InMemoryTrainingDateRepository) FindByID(id TrainingDateID) (*TrainingDate, error) { r.lock.RLock() defer r.lock.RUnlock() date, ok := r.trainingDates[id] if !ok { return nil, ErrTrainingDateNotFound } return &date, nil } func (r *InMemoryTrainingDateRepository) FindAll() ([]TrainingDate, error) { r.lock.RLock() defer r.lock.RUnlock() dates := make([]TrainingDate, len(r.trainingDates)) for _, date := range r.trainingDates { dates = append(dates, date) } return dates, nil } func (r *InMemoryTrainingDateRepository) FindAllByTrainingID(trainingID TrainingID) ([]TrainingDate, error) { r.lock.RLock() defer r.lock.RUnlock() dates := make([]TrainingDate, 0) for _, id := range r.trainingToDates[trainingID] { dates = append(dates, r.trainingDates[id]) } return dates, nil } func (r *InMemoryTrainingDateRepository) FindUpcomingByTrainingID(trainingID TrainingID) ([]TrainingDate, error) { r.lock.RLock() defer r.lock.RUnlock() now := time.Now() var dates []TrainingDate for _, id := range r.trainingToDates[trainingID] { date := r.trainingDates[id] if date.Date.After(now) { dates = append(dates, date) } } return dates, nil } func (r *InMemoryTrainingDateRepository) FindAllUpcoming() ([]TrainingDate, error) { r.lock.RLock() defer r.lock.RUnlock() now := time.Now() var dates []TrainingDate for _, date := range r.trainingDates { if date.Date.After(now) { dates = append(dates, date) } } return dates, nil } func (r *InMemoryTrainingDateRepository) Update(trainingDate *TrainingDate) error { r.lock.Lock() defer r.lock.Unlock() oldDate := r.trainingDates[trainingDate.ID] trainingDate.trainingID = oldDate.trainingID r.trainingDates[trainingDate.ID] = *trainingDate return nil } func (r *InMemoryTrainingDateRepository) Delete(id TrainingDateID) error { r.lock.Lock() defer r.lock.Unlock() date, ok := r.trainingDates[id] if !ok { return ErrTrainingDateNotFound } delete(r.trainingDates, id) dates := r.trainingToDates[date.trainingID] for idx, d := range dates { if d == id { r.trainingToDates[date.trainingID] = append(dates[:idx], dates[idx+1:]...) break } } return nil }