1
0
Fork 0

feat(training): add pricing and TrainingPrice with amount, currency and type (open|corporate)

This commit is contained in:
Vojtěch Mareš 2024-05-05 11:55:33 +02:00
parent db6ff45c99
commit 5d83bb0308
Signed by: vojtech.mares
GPG key ID: C6827B976F17240D
8 changed files with 316 additions and 45 deletions

View file

@ -163,10 +163,15 @@ components:
maximum: 5
description:
type: string
pricing:
type: array
items:
$ref: "#/components/schemas/TrainingPrice"
required:
- name
- days
- description
- pricing
Training:
allOf:
@ -185,6 +190,34 @@ components:
x-go-type-import:
path: gitlab.mareshq.com/hq/yggdrasil/pkg/training
TrainingPrice:
type: object
properties:
currency:
type: string
enum:
- CZK
- EUR
- USD
x-go-type: currency.Currency
x-go-type-package:
path: gitlab.mareshq.com/hq/yggdrasil/internal/currency
amount:
type: number
minimum: 0
x-go-type: decimal.Decimal
x-go-type-package:
path: github.com/shopspring/decimal
type:
type: string
enum:
- OPEN
- CORPORATE
required:
- currency
- amount
- type
ProblemDetails:
type: object
description: >

1
go.mod
View file

@ -40,6 +40,7 @@ require (
github.com/oklog/ulid v1.3.1 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.52.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect

2
go.sum
View file

@ -112,6 +112,8 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=

View file

@ -0,0 +1,13 @@
package currency
type Currency string
const (
USD Currency = "USD"
EUR Currency = "EUR"
CZK Currency = "CZK"
)
var (
AllowedCurrencies = []Currency{USD, EUR, CZK}
)

View file

@ -16,9 +16,17 @@ import (
"github.com/getkin/kin-openapi/openapi3"
"github.com/gofiber/fiber/v2"
"github.com/oapi-codegen/runtime"
"github.com/shopspring/decimal"
"gitlab.mareshq.com/hq/yggdrasil/internal/currency"
"gitlab.mareshq.com/hq/yggdrasil/pkg/training"
)
// Defines values for TrainingPriceType.
const (
CORPORATE TrainingPriceType = "CORPORATE"
OPEN TrainingPriceType = "OPEN"
)
// CreateTrainingRequest defines model for CreateTrainingRequest.
type CreateTrainingRequest = NewTraining
@ -33,9 +41,10 @@ type ListTrainingsResponse = []Training
// NewTraining defines model for NewTraining.
type NewTraining struct {
Days int8 `json:"days"`
Description string `json:"description"`
Name string `json:"name"`
Days int8 `json:"days"`
Description string `json:"description"`
Name string `json:"name"`
Pricing []TrainingPrice `json:"pricing"`
}
// ProblemDetails Schema that carries the details of an error in an HTTP response. See https://datatracker.ietf.org/doc/html/rfc7807 for more information.
@ -58,15 +67,26 @@ type ProblemDetails struct {
// Training defines model for Training.
type Training struct {
Days int8 `json:"days"`
Description string `json:"description"`
Id TrainingID `json:"id"`
Name string `json:"name"`
Days int8 `json:"days"`
Description string `json:"description"`
Id TrainingID `json:"id"`
Name string `json:"name"`
Pricing []TrainingPrice `json:"pricing"`
}
// TrainingID defines model for TrainingID.
type TrainingID = training.TrainingID
// TrainingPrice defines model for TrainingPrice.
type TrainingPrice struct {
Amount decimal.Decimal `json:"amount"`
Currency currency.Currency `json:"currency"`
Type TrainingPriceType `json:"type"`
}
// TrainingPriceType defines model for TrainingPrice.Type.
type TrainingPriceType string
// UpdateTrainingRequest defines model for UpdateTrainingRequest.
type UpdateTrainingRequest = NewTraining
@ -585,27 +605,29 @@ func (sh *strictHandler) UpdateTraining(ctx *fiber.Ctx, trainingID TrainingID) e
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
"H4sIAAAAAAAC/9xYwXLbNhD9FQzaW2lRbpJJhqemdZtoJk3cxOnF9QEClyQcEoCBpWyNhl/SU/+l/a8O",
"AJEiJdqRG2Xc5GSaBHbfvl087GpFuaq0kiDR0mRFNTOsAgTj/zszTEgh89mJ+y8Fy43QKJSkSfeNzE5o",
"RIV7pRkWNKKSVUATipvNETVwVQsDKU3Q1BBRywuomLP6rYGMJvSbeAMkDl9t3PPfNI2zYrWSFjy4mUQw",
"kpU/G6OMe8GVRJDoHpnWpeDMQY21UfMSqu8urcO92tP1adh1AshEaYP7IQGtfwIeQBPRmVywUqQzqWt8",
"OFQeAxEOxAbaa4W/qFqmDwXrtUKSOQAtpKatAZ/LnwwwhDbdb+GqBuuxaaM0GBQh5Slb+r+ZMhVDmlAh",
"8RmNaMVuRFVXNHkS0UrI8HwcUVxqCKsgB8/DANWqXWDRCJm776F2dz40/Qo+D6uiAGdo86JzquaXwNHZ",
"3A4u1LBnvizfZDQ5v5vd13DdbqZNtNpxsM2SSO95rlaD4ERKL5qLJqIvAL880K+E7VDbPmyBUNl9fbi0",
"rREzY9jSH6FeRF9JZW6d2x2Nf+dpIVgwJJwZI8ASLICkYQNRGWEynGgipHt+eXZ2SlqZnpB3AKRA1DaJ",
"45QhQ8P4BzATAZhNlMnjVPG4wKqMTcafPps+JZkypFIGiJCBS6Hk5A9Jo23CPYRdyM9JUVdMHhlgKZuX",
"QOBGl0x6O8Rq4CITnKAiWAhLFOe1MSA5uFhcaGsBnNBoNwVCWmSSw5jX929nxEAGwZhnTKQgUWQtaZ3z",
"+zm1yLAeyc1ZAYHtsIBwlQLJQYJhCCmZL71lZUQuJLFgFmA8uXvH3atNFFjCHlzbuqqYWW7ZJM7gaGzh",
"xX8h8yOmt06F/9qG0VEatUXUy+zYKemf+v+3+A37tU6L6lqkOxxF9OYoV0frl22zNjnrd23diiNRaWXC",
"jeyavITmAks2n1TMgC2uJlxVcXEVL/M8NcyKMtYf8hg7DpqIvtfp13vFbwf3ZdyWjde0TLX9IOM+IVB5",
"ZaWCVT8s1CUCL3yaXZI37f3v6hL//pMX5Fdm4J+/aERr43a1cn99fT3Z2d1EtBQc1tysLZ0apY0AZGbZ",
"Exvq7NqXv5HnpzMa0QUYG+RhOjmeTN1CpUEyLWhCH/lXka9Nz0+8OO6qz7/IwYfmOPRXwSylybBXoFvj",
"xffT6R198v364/GmZKRNfk5KYdHrZ4erieiTgGXMRYc5Hs5DvsEOcryOlLCy7JmNKDLHzTndUOA0RCs7",
"QtWwh10PdGDxR5UuD0bT+BTQDI+mmx+bnVwdfzYQtyerm4C535K6TD3eL1Pbc+IhchyAE0YkXHd5viXN",
"TTQ8IvFqM6034UYuAWG3Dk78+14d9H8wuEXkNksGqnSxk8THd/zKEACtOX78caaGA+8h+A2hE9Zx63os",
"f02OH6RRyenNVAcm73BqNTb3jWoV9qalB0rKC8D9M6LrkYwMr+5PT8rhZXG8c9pLFqefDcQeslj7LZ8o",
"iw9UVyHcfUvL7/UjVqiZISGvFGclSWEBpdIVSFyPY4OWKYnj0q0rlMXk0XQ69eW0drZt8U1bwZawuapx",
"cK2vu6oNvuai+TcAAP//9nxvI+cVAAA=",
"H4sIAAAAAAAC/+xYzXLbthN/FQz+/1tpUW6SSYanurabaJraqmP3UNcHCFyJcEgABpZ2NB4+SU99l/a9",
"OgBIipQYW06ccTqTkyh8LH7728V+4JZyVWglQaKlyS3VzLACEIz/d2qYkEIuJgfuXwqWG6FRKEmTdo5M",
"DmhEhRvSDDMaUckKoAnF1eaIGrgqhYGUJmhKiKjlGRTMSf2/gTlN6P/iFZA4zNq4c35VVU6K1Upa8OAm",
"EsFIlh8ao4wb4EoiSHSfTOtccOagxtqoWQ7Fd5fW4b7d8uhp2HUAyERuw/F9AprzCXgAVUQn8prlIp1I",
"XeLTofIYiHAgVtCOFP6kSpk+FawjhWTuADSQqsYHvC33DTCExtwncFWC9di0URoMimDylC3971yZgiFN",
"qJD4ika0YB9EURY0eRHRQsjwvRtRXGoIq2ABnoceqttmgUUj5MLNB98dmNBGcPeZ3FKBUNhtXXdqBAcn",
"oJbIjGFLGpy5uRLn4dgo6NcHuTr5opWhZpfA0Qld5y1cD2/UPD+e0+T8bpRHcNNsplW0TrdIH3RB+yqJ",
"dADxRRXR14D/LcBvhW0R2y7kBznCpg9EtKvNN2e/z9nXosxGRnrn0RDMGBLOjBFgCWZA0rCBqDlhMsQf",
"IqT7fnN6OiVNUhmRdwAkQ9Q2ieOUIUPD+HswIwE4HymziFPF4wyLPDZz/vLV+CWZK0MKZYAIGcwklBz9",
"ERTp2dJD2IS8R7KyYHLHAEvZLAcCH3TOpJdDrAYu5oITVAQzYYnivDQGJAeni1OtDtcjGm0aUUiLTHIY",
"OvXsZEIMzCEI84yJFCSKeUNae/jDDrXIsBywzWkGge2wgHCVAlmABMMQUjJbesnKiIWQxIK5BuPJ3Vrv",
"jtujwBy24NqWRcHMck0mcQIHdQsDn0LmPaLX7oefbdRoKY0aJ+pYduiWdAPK1xtT+5VlG+LKUqQb/ET0",
"w85C7dSDTVk5Ou3Wl+2KHVFoZULt4MrRhC4E5mw2KpgBm12NuCri7CpeLhapYVbksX6/iLHVv4MtRLON",
"uMwKVYbCqY2+4xayLIsZmDXIKXBRsHx0EH57cDXj79kC+nizcuZx2kxpqx0JcS3DA6xvw9JtAukAnNP9",
"33+mET08O6ERPXt30KF9kMVGxGi/kXUvqDtJFHUxHLfYqs6NaUAeTw+PaET3j0+mxyd7p4ebKNcdiK/g",
"1bzXO4Zc/0yn36rIT0is67x9/UVZ5fPbXDWdDOPezlD4LEsFK364VpcIPPMe6/x11Zj+pi7x7z95Rn5h",
"Bv75i0a0NG5Xk/pvbm5GG7uriOaCQ81LLWlqlDYCkJllJ/FQJ9e++ZXsTSc0otdgbEgV49HuaOwWKg2S",
"aUET+swPRf6aebbi6902GvmBBXjVHKO+LJikNOmXpHStMf5+PL6jw3tYZzdc+w40eHskFxZ9Lm1xVRF9",
"EbAMHdFijvudvG8NQ2quNSUszztiI4rMcXNOVxS4nKKVHaCq3yLVTxFg8UeVLh+NpuH+tep7M5oSqg1b",
"7X4xEB83Vvt2w/2W1Fnq+XaWWn/heAwbB+CEEQk3rZ0/YuYq6l+R+Hb1zlSF6iwHhE0/OPDjHT/oPnV9",
"JMCtlvRi1MWGEZ/f8T4WANUcP7+fqf5TzWPwG1QnrOXW1du+bBq+SIMhp9O2PzJ5jxethp4WBmMVdpry",
"JzLKa8DtLaLLAYv00/bnG+Xxw+JwQbZVWBx/MRBbhMXSb/nMsPhEfhXU3da1/F7fbgef6RPyVnGWkxSu",
"IVe6AIl1a94rmZI4zt26TFlMno3HY+9O9WHrEo8bD7aEzVSJvbReV1UrfNVF9W8AAAD//yH0TvuhGAAA",
}
// GetSwagger returns the content of the embedded swagger specification file

View file

@ -29,12 +29,21 @@ func (h *APIHandlers) ListTrainings(ctx context.Context, req ListTrainingsReques
}
data := make([]Training, len(trainings))
for idx, training := range trainings {
for idx, t := range trainings {
pricing := make([]TrainingPrice, len(t.Pricing))
for idx, p := range t.Pricing {
pricing[idx] = TrainingPrice{
Amount: p.Amount,
Currency: p.Currency,
Type: TrainingPriceType(p.Type),
}
}
data[idx] = Training{
Id: training.ID,
Name: training.Name,
Days: training.Days,
Description: training.Description,
Id: t.ID,
Name: t.Name,
Days: t.Days,
Description: t.Description,
Pricing: pricing,
}
}
@ -42,10 +51,20 @@ func (h *APIHandlers) ListTrainings(ctx context.Context, req ListTrainingsReques
}
func (h *APIHandlers) CreateTraining(ctx context.Context, req CreateTrainingRequestObject) (CreateTrainingResponseObject, error) {
pricing := make([]training.TrainingPrice, len(req.Body.Pricing))
for idx, p := range req.Body.Pricing {
pricing[idx] = training.TrainingPrice{
Amount: p.Amount,
Currency: p.Currency,
Type: training.TrainingPriceType(p.Type),
}
}
t := training.Training{
Name: req.Body.Name,
Days: req.Body.Days,
Description: req.Body.Description,
Pricing: pricing,
}
err := h.trainingRepository.Create(&t)
@ -58,11 +77,21 @@ func (h *APIHandlers) CreateTraining(ctx context.Context, req CreateTrainingRequ
}}, nil
}
responsePricing := make([]TrainingPrice, len(t.Pricing))
for idx, p := range t.Pricing {
responsePricing[idx] = TrainingPrice{
Amount: p.Amount,
Currency: p.Currency,
Type: TrainingPriceType(p.Type),
}
}
return CreateTraining201JSONResponse{
Id: t.ID,
Name: t.Name,
Days: t.Days,
Description: t.Description,
Pricing: responsePricing,
}, nil
}
@ -103,20 +132,38 @@ func (h *APIHandlers) GetTraining(ctx context.Context, req GetTrainingRequestObj
}}, nil
}
pricing := make([]TrainingPrice, len(t.Pricing))
for idx, p := range t.Pricing {
pricing[idx] = TrainingPrice{
Amount: p.Amount,
Currency: p.Currency,
Type: TrainingPriceType(p.Type),
}
}
return GetTraining200JSONResponse{
Id: t.ID,
Name: t.Name,
Days: t.Days,
Description: t.Description,
Pricing: pricing,
}, nil
}
func (h *APIHandlers) UpdateTraining(ctx context.Context, req UpdateTrainingRequestObject) (UpdateTrainingResponseObject, error) {
pricing := make([]training.TrainingPrice, len(req.Body.Pricing))
for idx, p := range req.Body.Pricing {
pricing[idx] = training.TrainingPrice{
Amount: p.Amount,
Currency: p.Currency,
Type: training.TrainingPriceType(p.Type),
}
}
t := training.Training{
ID: req.TrainingID,
Name: req.Body.Name,
Days: req.Body.Days,
Description: req.Body.Description,
Pricing: pricing,
}
err := h.trainingRepository.Update(&t)

View file

@ -2,6 +2,8 @@ package training
import (
"github.com/google/uuid"
"github.com/shopspring/decimal"
"gitlab.mareshq.com/hq/yggdrasil/internal/currency"
)
type TrainingID = uuid.UUID
@ -15,4 +17,18 @@ type Training struct {
Name string
Days int8
Description string
Pricing []TrainingPrice
}
type TrainingPrice struct {
Amount decimal.Decimal
Currency currency.Currency
Type TrainingPriceType
}
type TrainingPriceType string
const (
OpenTrainingPrice TrainingPriceType = "OPEN"
CorporateTrainingPrice TrainingPriceType = "CORPORATE"
)

View file

@ -1,5 +1,10 @@
package training
import (
"github.com/shopspring/decimal"
"gitlab.mareshq.com/hq/yggdrasil/internal/currency"
)
type TrainingRepository interface {
Create(training *Training) error
FindByID(id TrainingID) (*Training, error)
@ -17,35 +22,167 @@ func NewInMemoryTrainingRepository() *InMemoryTrainingRepository {
trainings: make(map[TrainingID]Training),
}
repo.Create(&Training{
_ = repo.Create(&Training{
Name: "Kubernetes",
Days: 2,
Description: "",
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{
_ = repo.Create(&Training{
Name: "Terraform",
Days: 1,
Description: "",
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{
_ = repo.Create(&Training{
Name: "RKE2",
Days: 1,
Description: "",
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{
_ = repo.Create(&Training{
Name: "GitHub Actions",
Days: 1,
Description: "",
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{
_ = repo.Create(&Training{
Name: "GitLab CI",
Days: 1,
Description: "",
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{
_ = repo.Create(&Training{
Name: "Prometheus",
Days: 2,
Description: "",
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