Compare commits
10 commits
3b95a216bd
...
49e05cac10
| Author | SHA1 | Date | |
|---|---|---|---|
| 49e05cac10 | |||
| 7ed1e05284 | |||
| cb9cc47245 | |||
| ab4226561b | |||
| b437b64a94 | |||
| 9c4d42c26d | |||
| c2454a8298 | |||
| c877fa0d2c | |||
| c340623721 | |||
| a2d47a4b3e |
31 changed files with 2277 additions and 263 deletions
34
.redocly.lint-ignore.yaml
Normal file
34
.redocly.lint-ignore.yaml
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.
|
||||
# See https://redoc.ly/docs/cli/ for more information.
|
||||
api/v1/openapi.yaml:
|
||||
info-license-url:
|
||||
- '#/info/license/url'
|
||||
no-server-example.com:
|
||||
- '#/servers/0/url'
|
||||
operation-4xx-response:
|
||||
- '#/paths/~1trainings/get/responses'
|
||||
- '#/paths/~1trainings~1{trainingID}~1dates/get/responses'
|
||||
- >-
|
||||
#/paths/~1trainings~1{trainingID}~1dates~1{dateID}~1attendees/get/responses
|
||||
- '#/paths/~1trainings~1{trainingID}~1dates~1{dateID}~1feedback/get/responses'
|
||||
- '#/paths/~1trainings~1{trainingID}~1feedback/get/responses'
|
||||
- '#/paths/~1trainings~1upcoming-dates/get/responses'
|
||||
security-defined:
|
||||
- '#/paths/~1trainings/get'
|
||||
- '#/paths/~1trainings/post'
|
||||
- '#/paths/~1trainings~1{trainingID}/get'
|
||||
- '#/paths/~1trainings~1{trainingID}/put'
|
||||
- '#/paths/~1trainings~1{trainingID}/delete'
|
||||
- '#/paths/~1trainings~1{trainingID}~1dates/get'
|
||||
- '#/paths/~1trainings~1{trainingID}~1dates/post'
|
||||
- '#/paths/~1trainings~1{trainingID}~1dates~1{dateID}/put'
|
||||
- '#/paths/~1trainings~1{trainingID}~1dates~1{dateID}/delete'
|
||||
- '#/paths/~1trainings~1{trainingID}~1dates~1{dateID}~1attendees/get'
|
||||
- '#/paths/~1trainings~1{trainingID}~1dates~1{dateID}~1attendees/post'
|
||||
- >-
|
||||
#/paths/~1trainings~1{trainingID}~1dates~1{dateID}~1attendees~1{attendeeID}/delete
|
||||
- >-
|
||||
#/paths/~1trainings~1{trainingID}~1dates~1{dateID}~1attendees~1{attendeeID}~1feedback/post
|
||||
- '#/paths/~1trainings~1{trainingID}~1dates~1{dateID}~1feedback/get'
|
||||
- '#/paths/~1trainings~1{trainingID}~1feedback/get'
|
||||
- '#/paths/~1trainings~1upcoming-dates/get'
|
||||
20
Makefile
20
Makefile
|
|
@ -1,3 +1,21 @@
|
|||
.PHONY: generate
|
||||
generate:
|
||||
oapi-codegen -config ./oapi-codegen.yaml ./api/v1/openapi.yaml
|
||||
go generate .
|
||||
|
||||
POSTGRES_URL="postgres://official:backoffice@localhost:5432/backoffice_dev?sslmode=disable"
|
||||
|
||||
.PHONY: local-migrate-up
|
||||
local-migrate-up:
|
||||
migrate -database ${POSTGRES_URL} -path ./db/migrations up
|
||||
|
||||
.PHONY: local-migrate-force
|
||||
local-migrate-force:
|
||||
migrate -database ${POSTGRES_URL} -path ./db/migrations force
|
||||
|
||||
.PHONY: local-migrate-down
|
||||
local-migrate-down:
|
||||
migrate -database ${POSTGRES_URL} -path ./db/migrations down
|
||||
|
||||
.PHONY: local-migrate-drop
|
||||
local-migrate-drop:
|
||||
migrate -database ${POSTGRES_URL} -path ./db/migrations drop -f
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
openapi: "3.1.0"
|
||||
info:
|
||||
version: 1.3.0
|
||||
x-go-package: "github.com/oapi-codegen/runtime"
|
||||
title: Backoffice API
|
||||
license:
|
||||
name: Proprietary
|
||||
|
|
@ -8,6 +9,8 @@ info:
|
|||
name: Vojtěch Mareš
|
||||
email: iam@vojtechmares.com
|
||||
url: https://www.vojtechmares.com
|
||||
x-oapi-codegen-middlewares:
|
||||
- Middleware
|
||||
servers:
|
||||
- url: http://localhost:8080/v1
|
||||
description: Local development server
|
||||
|
|
@ -402,12 +405,18 @@ paths:
|
|||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
x-go-type: training.ID
|
||||
x-go-type-import:
|
||||
path: gitlab.mareshq.com/hq/backoffice/backoffice-api/pkg/training
|
||||
- name: dateID
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
x-go-type: training.DateID
|
||||
x-go-type-import:
|
||||
path: gitlab.mareshq.com/hq/backoffice/backoffice-api/pkg/training
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
|
|
@ -484,139 +493,6 @@ paths:
|
|||
schema:
|
||||
$ref: "#/components/schemas/ProblemDetails"
|
||||
|
||||
/trainings/{trainingID}/dates/{dateID}/attendees/{attendeeID}/feedback:
|
||||
post:
|
||||
summary: Submit feedback for an attendee of a date of a training
|
||||
operationId: createTrainingDateAttendeeFeedback
|
||||
tags:
|
||||
- training attendee feedback
|
||||
parameters:
|
||||
- name: trainingID
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
x-go-type: training.ID
|
||||
x-go-type-import:
|
||||
path: gitlab.mareshq.com/hq/backoffice/backoffice-api/pkg/training
|
||||
- name: dateID
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
x-go-type: training.DateID
|
||||
x-go-type-import:
|
||||
path: gitlab.mareshq.com/hq/backoffice/backoffice-api/pkg/training
|
||||
- name: attendeeID
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
x-go-type: training.AttendeeID
|
||||
x-go-type-import:
|
||||
path: gitlab.mareshq.com/hq/backoffice/backoffice-api/pkg/training
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/NewTrainingFeedback"
|
||||
responses:
|
||||
"201":
|
||||
description: Feedback created
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/TrainingFeedback"
|
||||
"409":
|
||||
description: Feedback already submitted
|
||||
content:
|
||||
application/problem+json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ProblemDetails"
|
||||
"500":
|
||||
description: Internal error
|
||||
content:
|
||||
application/problem+json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ProblemDetails"
|
||||
|
||||
/trainings/{trainingID}/dates/{dateID}/feedback:
|
||||
get:
|
||||
summary: List all feedback of a date of a training
|
||||
operationId: listTrainingDateFeedback
|
||||
tags:
|
||||
- training attendee feedback
|
||||
parameters:
|
||||
- name: trainingID
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
x-go-type: training.ID
|
||||
x-go-type-import:
|
||||
path: gitlab.mareshq.com/hq/backoffice/backoffice-api/pkg/training
|
||||
- name: dateID
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
x-go-type: training.DateID
|
||||
x-go-type-import:
|
||||
path: gitlab.mareshq.com/hq/backoffice/backoffice-api/pkg/training
|
||||
responses:
|
||||
"200":
|
||||
description: A list of feedback
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/TrainingFeedback"
|
||||
"500":
|
||||
description: Internal error
|
||||
content:
|
||||
application/problem+json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ProblemDetails"
|
||||
|
||||
/trainings/{trainingID}/feedback:
|
||||
get:
|
||||
summary: List all feedback of a training
|
||||
operationId: listTrainingFeedback
|
||||
tags:
|
||||
- training attendee feedback
|
||||
parameters:
|
||||
- name: trainingID
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
x-go-type: training.ID
|
||||
x-go-type-import:
|
||||
path: gitlab.mareshq.com/hq/backoffice/backoffice-api/pkg/training
|
||||
responses:
|
||||
"200":
|
||||
description: A list of feedback
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/TrainingFeedback"
|
||||
"500":
|
||||
description: Internal error
|
||||
content:
|
||||
application/problem+json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ProblemDetails"
|
||||
|
||||
/trainings/upcoming-dates:
|
||||
get:
|
||||
summary: List all upcoming dates of all trainings
|
||||
|
|
@ -670,22 +546,14 @@ components:
|
|||
properties:
|
||||
name:
|
||||
type: string
|
||||
duration:
|
||||
days:
|
||||
type: integer
|
||||
format: int32
|
||||
price:
|
||||
type: object
|
||||
properties:
|
||||
open:
|
||||
$ref: "#/components/schemas/Price"
|
||||
corporate:
|
||||
$ref: "#/components/schemas/Price"
|
||||
length:
|
||||
type: integer
|
||||
format: int32
|
||||
$ref: "#/components/schemas/TrainingPrice"
|
||||
required:
|
||||
- name
|
||||
- duration
|
||||
- days
|
||||
- price
|
||||
Training:
|
||||
allOf:
|
||||
|
|
@ -701,6 +569,31 @@ components:
|
|||
required:
|
||||
- id
|
||||
|
||||
TrainingPrice:
|
||||
types: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
- price
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
enum: [open, corporate]
|
||||
price:
|
||||
type: object
|
||||
required:
|
||||
- currency
|
||||
- amount
|
||||
properties:
|
||||
currency:
|
||||
type: string
|
||||
enum: [CZK, EUR, USD]
|
||||
amount:
|
||||
type: number
|
||||
format: float64
|
||||
minimum: 0
|
||||
|
||||
NewTrainingDate:
|
||||
type: object
|
||||
properties:
|
||||
|
|
@ -767,40 +660,6 @@ components:
|
|||
required:
|
||||
- id
|
||||
|
||||
NewTrainingFeedback:
|
||||
type: object
|
||||
properties:
|
||||
rating:
|
||||
type: integer
|
||||
format: int32
|
||||
minimum: 0
|
||||
maximum: 10
|
||||
comment:
|
||||
type: string
|
||||
anonymous:
|
||||
type: boolean
|
||||
default: false
|
||||
isSharingAllowed:
|
||||
type: boolean
|
||||
default: false
|
||||
required:
|
||||
- rating
|
||||
- comment
|
||||
|
||||
TrainingFeedback:
|
||||
allOf:
|
||||
- $ref: "#/components/schemas/NewTrainingFeedback"
|
||||
- type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
x-go-type: training.FeedbackID
|
||||
x-go-type-import:
|
||||
path: gitlab.mareshq.com/hq/backoffice/backoffice-api/pkg/training
|
||||
required:
|
||||
- id
|
||||
|
||||
Price:
|
||||
type: object
|
||||
properties:
|
||||
|
|
|
|||
20
cmd/migrate.go
Normal file
20
cmd/migrate.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
}
|
||||
|
||||
var migrateCmd = &cobra.Command{
|
||||
Use: "migrate",
|
||||
Short: "Migrate database schema up/down",
|
||||
Long: `All software has versions. This is backoffice backend's`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("Migrate database schema up/down")
|
||||
},
|
||||
}
|
||||
26
cmd/migrate_up.go
Normal file
26
cmd/migrate_up.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
migrateCmd.AddCommand(migrateUpCmd)
|
||||
}
|
||||
|
||||
var migrateUpCmd = &cobra.Command{
|
||||
Use: "up",
|
||||
Short: "Migrate database schema up",
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// TODO: Implement this
|
||||
|
||||
// TODO: get database URL
|
||||
|
||||
// TODO: run migrations
|
||||
|
||||
// TODO: handle errors
|
||||
|
||||
// TODO: gracefully shutdown
|
||||
},
|
||||
}
|
||||
20
cmd/server.go
Normal file
20
cmd/server.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"gitlab.mareshq.com/hq/backoffice/backoffice-api/internal/server"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(serverCmd)
|
||||
}
|
||||
|
||||
var serverCmd = &cobra.Command{
|
||||
Use: "server",
|
||||
Short: "Starts backoffice backend API server",
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
srv := server.NewServer()
|
||||
srv.Run()
|
||||
},
|
||||
}
|
||||
21
cmd/version.go
Normal file
21
cmd/version.go
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"gitlab.mareshq.com/hq/backoffice/backoffice-api/internal/version"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
}
|
||||
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the version number of backoffice backend",
|
||||
Long: `All software has versions. This is backoffice backend's`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Version: %s (commit: %s)\n", version.Version, version.Commit)
|
||||
},
|
||||
}
|
||||
8
db/migrations/20240329172935_init.down.sql
Normal file
8
db/migrations/20240329172935_init.down.sql
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
BEGIN;
|
||||
|
||||
DROP TABLE IF EXISTS training.attendees;
|
||||
DROP TABLE IF EXISTS training.dates;
|
||||
DROP TABLE IF EXISTS training.trainings;
|
||||
DROP SCHEMA IF EXISTS training;
|
||||
|
||||
COMMIT;
|
||||
62
db/migrations/20240329172935_init.up.sql
Normal file
62
db/migrations/20240329172935_init.up.sql
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
BEGIN;
|
||||
|
||||
CREATE SCHEMA IF NOT EXISTS training;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS training.trainings (
|
||||
id UUID PRIMARY KEY,
|
||||
name varchar(255) NOT NULL,
|
||||
description text NOT NULL,
|
||||
days smallint NOT NULL,
|
||||
price decimal NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS training.prices (
|
||||
training_id UUID REFERENCES training.trainings(id),
|
||||
amount NUMERIC(6, 4) NOT NULL,
|
||||
currency VARCHAR(3) NOT NULL,
|
||||
CHECK (currency IN ('USD', 'EUR', 'CZK'))
|
||||
type VARCHAR(10) NOT NULL,
|
||||
CHECK (type IN ('OPEN', 'CORPORATE', 'STUDENT', 'GOVERNMENT'))
|
||||
PRIMARY KEY (training_id, currency, type) -- composite primary key
|
||||
)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS training.dates (
|
||||
id UUID PRIMARY KEY,
|
||||
training_id UUID NOT NULL,
|
||||
date DATE NOT NULL,
|
||||
start_time TIME NOT NULL,
|
||||
days SMALLINT NOT NULL,
|
||||
is_online BOOLEAN NOT NULL,
|
||||
location VARCHAR(255) NOT NULL,
|
||||
address VARCHAR(255) NOT NULL,
|
||||
capacity SMALLINT NOT NULL,
|
||||
amount NUMERIC(6, 4) NOT NULL,
|
||||
currency VARCHAR(3) NOT NULL,
|
||||
CHECK (currency IN ('USD', 'EUR', 'CZK'))
|
||||
FOREIGN KEY (training_id) REFERENCES training.trainings(id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS training.attendees (
|
||||
id UUID PRIMARY KEY,
|
||||
date_id UUID NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
company VARCHAR(255) NOT NULL,
|
||||
role VARCHAR(255) NOT NULL,
|
||||
is_student BOOLEAN NOT NULL,
|
||||
has_attended BOOLEAN NOT NULL,
|
||||
has_paid BOOLEAN NOT NULL,
|
||||
FOREIGN KEY (date_id) REFERENCES training.dates(id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS training.feedback (
|
||||
id UUID PRIMARY KEY,
|
||||
attendee_id UUID NOT NULL,
|
||||
rating SMALLINT NOT NULL,
|
||||
comment TEXT NOT NULL,
|
||||
is_anonymous BOOLEAN NOT NULL,
|
||||
is_sharing_allowed BOOLEAN NOT NULL,
|
||||
FOREIGN KEY (attendee_id) REFERENCES training.attendees(id)
|
||||
);
|
||||
|
||||
COMMIT;
|
||||
55
go.mod
55
go.mod
|
|
@ -1,3 +1,58 @@
|
|||
module gitlab.mareshq.com/hq/backoffice/backoffice-api
|
||||
|
||||
go 1.22.0
|
||||
|
||||
require (
|
||||
github.com/deepmap/oapi-codegen/v2 v2.1.0
|
||||
github.com/getkin/kin-openapi v0.122.0
|
||||
github.com/gofiber/contrib/fiberzap/v2 v2.1.3
|
||||
github.com/gofiber/contrib/swagger v1.1.2
|
||||
github.com/gofiber/fiber/v2 v2.52.4
|
||||
github.com/google/uuid v1.5.0
|
||||
github.com/oapi-codegen/fiber-middleware v1.0.1
|
||||
github.com/oapi-codegen/runtime v1.1.1
|
||||
github.com/spf13/cobra v1.8.0
|
||||
go.uber.org/zap v1.27.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/go-openapi/analysis v0.21.4 // indirect
|
||||
github.com/go-openapi/errors v0.20.4 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.20.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/loads v0.21.2 // indirect
|
||||
github.com/go-openapi/runtime v0.26.2 // indirect
|
||||
github.com/go-openapi/spec v0.20.11 // indirect
|
||||
github.com/go-openapi/strfmt v0.21.8 // indirect
|
||||
github.com/go-openapi/swag v0.22.4 // indirect
|
||||
github.com/go-openapi/validate v0.22.3 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/invopop/yaml v0.2.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.17.6 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // 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
|
||||
go.mongodb.org/mongo-driver v1.13.1 // indirect
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.12.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
|
|||
216
go.sum
216
go.sum
|
|
@ -0,0 +1,216 @@
|
|||
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deepmap/oapi-codegen/v2 v2.1.0 h1:I/NMVhJCtuvL9x+S2QzZKpSjGi33oDZwPRdemvOZWyQ=
|
||||
github.com/deepmap/oapi-codegen/v2 v2.1.0/go.mod h1:R1wL226vc5VmCNJUvMyYr3hJMm5reyv25j952zAVXZ8=
|
||||
github.com/getkin/kin-openapi v0.122.0 h1:WB9Jbl0Hp/T79/JF9xlSW5Kl9uYdk/AWD0yAd9HOM10=
|
||||
github.com/getkin/kin-openapi v0.122.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw=
|
||||
github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc=
|
||||
github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo=
|
||||
github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||
github.com/go-openapi/errors v0.20.4 h1:unTcVm6PispJsMECE3zWgvG4xTiKda1LIR5rCRWLG6M=
|
||||
github.com/go-openapi/errors v0.20.4/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ=
|
||||
github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA=
|
||||
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro=
|
||||
github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw=
|
||||
github.com/go-openapi/runtime v0.26.2 h1:elWyB9MacRzvIVgAZCBJmqTi7hBzU0hlKD4IvfX0Zl0=
|
||||
github.com/go-openapi/runtime v0.26.2/go.mod h1:O034jyRZ557uJKzngbMDJXkcKJVzXJiymdSfgejrcRw=
|
||||
github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
|
||||
github.com/go-openapi/spec v0.20.11 h1:J/TzFDLTt4Rcl/l1PmyErvkqlJDncGvPTMnCI39I4gY=
|
||||
github.com/go-openapi/spec v0.20.11/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
|
||||
github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg=
|
||||
github.com/go-openapi/strfmt v0.21.8 h1:VYBUoKYRLAlgKDrIxR/I0lKrztDQ0tuTDrbhLVP8Erg=
|
||||
github.com/go-openapi/strfmt v0.21.8/go.mod h1:adeGTkxE44sPyLk0JV235VQAO/ZXUr8KAzYjclFs3ew=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
|
||||
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/validate v0.22.3 h1:KxG9mu5HBRYbecRb37KRCihvGGtND2aXziBAv0NNfyI=
|
||||
github.com/go-openapi/validate v0.22.3/go.mod h1:kVxh31KbfsxU8ZyoHaDbLBWU5CnMdqBUEtadQ2G4d5M=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/gofiber/contrib/fiberzap/v2 v2.1.3 h1:znIDjHJUyhp11h5w8AaABCtEJejxjiCe47O0OC5We/g=
|
||||
github.com/gofiber/contrib/fiberzap/v2 v2.1.3/go.mod h1:9KrEpG4A8g1Y4J7OS5WYq6rDyy+sXAEDSgGdCGPnH1Y=
|
||||
github.com/gofiber/contrib/swagger v1.1.2 h1:qwyFR/yXEttI35JxAycC6KFuJu7rbhSsT1m4UkqGqis=
|
||||
github.com/gofiber/contrib/swagger v1.1.2/go.mod h1:o6hA4kifvR3xGNtAdWKNdSUGaWYtJ0Q5AWjsIHXLEWg=
|
||||
github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM=
|
||||
github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY=
|
||||
github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
|
||||
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/oapi-codegen/fiber-middleware v1.0.1 h1:DZQA+tEsqEMox4pPmc6SJXvDriiWSxc3QiA4UuMwGq4=
|
||||
github.com/oapi-codegen/fiber-middleware v1.0.1/go.mod h1:UUVrHLPT4Zv7S2U2TCGLXpTx6mfMb613UI+Z6F8S5Vk=
|
||||
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
|
||||
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
|
||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
|
||||
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
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=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0=
|
||||
github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ=
|
||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
|
||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
||||
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
|
||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8=
|
||||
go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk=
|
||||
go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
|
||||
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
File diff suppressed because it is too large
Load diff
126
internal/api/server.go
Normal file
126
internal/api/server.go
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"gitlab.mareshq.com/hq/backoffice/backoffice-api/internal/postgres"
|
||||
"gitlab.mareshq.com/hq/backoffice/backoffice-api/pkg/training"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type APIServer struct {
|
||||
logger *zap.SugaredLogger
|
||||
|
||||
trainingRepo *postgres.TrainingRepository
|
||||
dateRepo *postgres.DateRepository
|
||||
attendeeRepo *postgres.AttendeeRepository
|
||||
}
|
||||
|
||||
func NewAPIServer(db *sql.DB, logger *zap.SugaredLogger) *APIServer {
|
||||
return &APIServer{
|
||||
logger: logger,
|
||||
|
||||
trainingRepo: postgres.NewTrainingRepository(db),
|
||||
dateRepo: postgres.NewDateRepository(db),
|
||||
attendeeRepo: postgres.NewAttendeeRepository(db),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *APIServer) ListTrainings(c *fiber.Ctx) error {
|
||||
all, err := s.trainingRepo.FindAll()
|
||||
if err != nil {
|
||||
s.logger.Error("ListTrainings", zap.Error(err))
|
||||
c.JSON(ListTrainings500ApplicationProblemPlusJSONResponse{
|
||||
Status: fiber.StatusInternalServerError,
|
||||
Title: fmt.Sprintf("Internal server error: %s", "failed to list trainings"),
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(ListTrainings200JSONResponse{})
|
||||
}
|
||||
|
||||
func (s *APIServer) CreateTraining(c *fiber.Ctx) error {
|
||||
tr := &training.Training{}
|
||||
if err := c.BodyParser(tr); err != nil {
|
||||
s.logger.Error("CreateTraining", zap.Error(err))
|
||||
c.JSON(CreateTraining400ApplicationProblemPlusJSONResponse{
|
||||
Status: fiber.StatusBadRequest,
|
||||
Title: fmt.Sprintf("Internal server error: %s", "failed to parse request body"),
|
||||
})
|
||||
}
|
||||
|
||||
if err := s.trainingRepo.Create(tr); err != nil {
|
||||
s.logger.Error("CreateTraining", zap.Error(err))
|
||||
c.JSON(CreateTraining500ApplicationProblemPlusJSONResponse{
|
||||
Status: fiber.StatusInternalServerError,
|
||||
Title: fmt.Sprintf("Internal server error: %s", "failed to save training"),
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(CreateTraining201JSONResponse{
|
||||
Id: tr.ID,
|
||||
Name: tr.Name,
|
||||
Days: int32(tr.Days),
|
||||
Price: tr.Price,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (s *APIServer) UpdateTraining(c *fiber.Ctx) error {
|
||||
s.logger.Info("UpdateTraining")
|
||||
}
|
||||
|
||||
func (s *APIServer) DeleteTraining(c *fiber.Ctx, trainingID training.ID) error {
|
||||
s.logger.Info("DeleteTraining")
|
||||
}
|
||||
|
||||
func (s *APIServer) GetTraining(c *fiber.Ctx, trainingID training.ID) error {
|
||||
s.logger.Info("GetTraining")
|
||||
}
|
||||
|
||||
func (s *APIServer) ListTrainingDates(c *fiber.Ctx, trainingID training.ID) error {
|
||||
s.logger.Info("ListTrainingDates")
|
||||
}
|
||||
|
||||
func (s *APIServer) CreateTrainingDate(c *fiber.Ctx, trainingID training.ID) error {
|
||||
s.logger.Info("CreateTrainingDate")
|
||||
}
|
||||
|
||||
func (s *APIServer) UpdateTrainingDate(c *fiber.Ctx, trainingID training.ID, dateID training.DateID) error {
|
||||
s.logger.Info("UpdateTrainingDate")
|
||||
}
|
||||
|
||||
func (s *APIServer) DeleteTrainingDate(c *fiber.Ctx, trainingID training.ID, dateID training.DateID) error {
|
||||
s.logger.Info("DeleteTrainingDate")
|
||||
}
|
||||
|
||||
func (s *APIServer) ListUpcomingTrainingDates(c *fiber.Ctx, params ListUpcomingTrainingDatesParams) error {
|
||||
s.logger.Info("ListUpcomingTrainingDates")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *APIServer) ListTrainingDateAttendees(c *fiber.Ctx, trainingID training.ID, dateID training.DateID) error {
|
||||
s.logger.Info("ListTrainingDateAttendees")
|
||||
}
|
||||
|
||||
func (s *APIServer) CreateTrainingDateAttendee(c *fiber.Ctx, trainingID training.ID, dateID training.DateID) error {
|
||||
s.logger.Info("CreateTrainingDateAttendee")
|
||||
}
|
||||
|
||||
func (s *APIServer) DeleteTrainingDateAttendee(c *fiber.Ctx, trainingID training.ID, dateID training.DateID, attendeeID training.AttendeeID) error {
|
||||
s.logger.Info("DeleteTrainingDateAttendee")
|
||||
}
|
||||
|
||||
func (s *APIServer) CreateTrainingDateAttendeeFeedback(c *fiber.Ctx, trainingID training.ID, dateID training.DateID, attendeeID training.AttendeeID) error {
|
||||
s.logger.Info("CreateTrainingDateAttendeeFeedback")
|
||||
}
|
||||
|
||||
func (s *APIServer) ListTrainingDateFeedback(c *fiber.Ctx, trainingID training.ID, dateID training.DateID) error {
|
||||
s.logger.Info("ListTrainingDateFeedback")
|
||||
}
|
||||
|
||||
func (s *APIServer) ListTrainingFeedback(c *fiber.Ctx, trainingID training.ID) error {
|
||||
s.logger.Info("ListTrainingFeedback")
|
||||
}
|
||||
13
internal/currency/currency.go
Normal file
13
internal/currency/currency.go
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package currency
|
||||
|
||||
type Currency string
|
||||
|
||||
var (
|
||||
Currencies = []Currency{CZK, EUR, USD}
|
||||
)
|
||||
|
||||
const (
|
||||
CZK Currency = "CZK"
|
||||
EUR Currency = "EUR"
|
||||
USD Currency = "USD"
|
||||
)
|
||||
103
internal/postgres/attendee_repository.go
Normal file
103
internal/postgres/attendee_repository.go
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
package postgres
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"gitlab.mareshq.com/hq/backoffice/backoffice-api/pkg/training"
|
||||
)
|
||||
|
||||
type AttendeeRepository struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewAttendeeRepository(db *sql.DB) *AttendeeRepository {
|
||||
return &AttendeeRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *AttendeeRepository) Find(id training.AttendeeID) (*training.Attendee, error) {
|
||||
var a training.Attendee
|
||||
err := r.db.QueryRow("SELECT * FROM attendee WHERE id = $1", id).Scan(&a.ID, &a.Name, &a.DateID, &a.Email, &a.Company, &a.Role, &a.IsStudent, &a.HasAttended, &a.HasPaid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
func (r *AttendeeRepository) FindAll() ([]training.Attendee, error) {
|
||||
rows, err := r.db.Query("SELECT * FROM attendee")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var attendees []training.Attendee
|
||||
for rows.Next() {
|
||||
var a training.Attendee
|
||||
err := rows.Scan(&a.ID, &a.Name, &a.DateID, &a.Email, &a.Company, &a.Role, &a.IsStudent, &a.HasAttended, &a.HasPaid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attendees = append(attendees, a)
|
||||
}
|
||||
|
||||
return attendees, nil
|
||||
}
|
||||
|
||||
func (r *AttendeeRepository) FindAllForDate(dateID training.DateID) ([]training.Attendee, error) {
|
||||
rows, err := r.db.Query("SELECT * FROM attendee WHERE date_id = $1", dateID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var attendees []training.Attendee
|
||||
for rows.Next() {
|
||||
var a training.Attendee
|
||||
err := rows.Scan(&a.ID, &a.Name, &a.DateID, &a.Email, &a.Company, &a.Role, &a.IsStudent, &a.HasAttended, &a.HasPaid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
attendees = append(attendees, a)
|
||||
}
|
||||
|
||||
return attendees, nil
|
||||
}
|
||||
|
||||
func (r *AttendeeRepository) CountForDate(dateID training.DateID) (int, error) {
|
||||
var count int
|
||||
err := r.db.QueryRow("SELECT COUNT(*) FROM attendee WHERE date_id = $1", dateID).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (r *AttendeeRepository) Create(a *training.Attendee) error {
|
||||
a.ID = training.NewAttendeeID()
|
||||
_, err := r.db.Exec("INSERT INTO attendee (id, date_id, name, email, company, role, is_student, has_attended, has_paid) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", a.ID, a.DateID, a.Name, a.Email, a.Company, a.Role, a.IsStudent, a.HasAttended, a.HasPaid)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *AttendeeRepository) Update(a *training.Attendee) error {
|
||||
_, err := r.db.Exec("UPDATE attendee SET name = $1, email = $2, company = $3, role = $4, is_student = $5, has_attended = $6, has_paid = $7 WHERE id = $8", a.Name, a.Email, a.Company, a.Role, a.IsStudent, a.HasAttended, a.HasPaid, a.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *AttendeeRepository) UpdateAttendance(id training.AttendeeID, hasAttended bool) error {
|
||||
_, err := r.db.Exec("UPDATE attendee SET has_attended = $1 WHERE id = $2", hasAttended, id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *AttendeeRepository) UpdatePayment(id training.AttendeeID, hasPaid bool) error {
|
||||
_, err := r.db.Exec("UPDATE attendee SET has_paid = $1 WHERE id = $2", hasPaid, id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *AttendeeRepository) Delete(id training.AttendeeID) error {
|
||||
_, err := r.db.Exec("DELETE FROM attendee WHERE id = $1", id)
|
||||
return err
|
||||
}
|
||||
101
internal/postgres/date_repository.go
Normal file
101
internal/postgres/date_repository.go
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
package postgres
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"gitlab.mareshq.com/hq/backoffice/backoffice-api/pkg/training"
|
||||
)
|
||||
|
||||
type DateRepository struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewDateRepository(db *sql.DB) *DateRepository {
|
||||
return &DateRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *DateRepository) Get(id training.DateID) (*training.Date, error) {
|
||||
var d training.Date
|
||||
err := r.db.QueryRow("SELECT * FROM date WHERE id = $1", id).Scan(&d.ID, &d.Date, &d.TrainingID, &d.StartTime, &d.Days, &d.Price, &d.IsOnline, &d.Location, &d.Address, &d.Capacity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
func (r *DateRepository) FindAll() ([]training.Date, error) {
|
||||
rows, err := r.db.Query("SELECT * FROM date")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
var dates []training.Date
|
||||
for rows.Next() {
|
||||
var d training.Date
|
||||
err := rows.Scan(&d.ID, &d.Date, &d.TrainingID, &d.StartTime, &d.Days, &d.Price, &d.IsOnline, &d.Location, &d.Address, &d.Capacity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dates = append(dates, d)
|
||||
}
|
||||
|
||||
return dates, nil
|
||||
}
|
||||
|
||||
func (r *DateRepository) FindAllForTraining(id training.ID) ([]training.Date, error) {
|
||||
rows, err := r.db.Query("SELECT * FROM training.dates WHERE td.training_id = $1 ORDER BY date DESC", id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
|
||||
var dates []training.Date
|
||||
for rows.Next() {
|
||||
var d training.Date
|
||||
err := rows.Scan(&d.ID, &d.Date)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dates = append(dates, d)
|
||||
}
|
||||
|
||||
return dates, nil
|
||||
}
|
||||
|
||||
func (r *DateRepository) Create(d *training.Date) error {
|
||||
d.ID = training.NewDateID()
|
||||
_, err := r.db.Exec("INSERT INTO date (id, date, training_id, start_time, days, price, is_online, location, address, capacity) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", d.ID, d.Date, d.TrainingID, d.StartTime, d.Days, d.Price, d.IsOnline, d.Location, d.Address, d.Capacity)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *DateRepository) Update(d *training.Date) error {
|
||||
_, err := r.db.Exec("UPDATE date SET date = $1, start_time $2, days = $3, price = $4, is_online = $5, location = $6, address = $7, capacity = $8 WHERE id = $2", d.Date, d.StartTime, d.Days, d.Price, d.IsOnline, d.Location, d.Address, d.Capacity, d.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *DateRepository) Delete(id training.DateID) error {
|
||||
_, err := r.db.Exec("DELETE FROM date WHERE id = $1", id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *DateRepository) IsFull(id training.DateID) (bool, error) {
|
||||
var d training.Date
|
||||
err := r.db.QueryRow("SELECT * FROM date WHERE id = $1", id).Scan(&d.ID, &d.Date, &d.TrainingID, &d.StartTime, &d.Days, &d.Price, &d.IsOnline, &d.Location, &d.Address, &d.Capacity)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var count int8
|
||||
err = r.db.QueryRow("SELECT COUNT(*) FROM attendee WHERE date_id = $1", id).Scan(&count)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return count >= d.Capacity, nil
|
||||
}
|
||||
62
internal/postgres/training_repository.go
Normal file
62
internal/postgres/training_repository.go
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package postgres
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"gitlab.mareshq.com/hq/backoffice/backoffice-api/pkg/training"
|
||||
)
|
||||
|
||||
type TrainingRepository struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewTrainingRepository(db *sql.DB) *TrainingRepository {
|
||||
return &TrainingRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *TrainingRepository) Create(t *training.Training) error {
|
||||
t.ID = training.NewID()
|
||||
_, err := r.db.Exec("INSERT INTO training (id, name, days, description, price) VALUES ($1, $2, $3)", t.ID, t.Name, t.Days, t.Description, t.Price)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *TrainingRepository) Get(id training.ID) (*training.Training, error) {
|
||||
var t training.Training
|
||||
err := r.db.QueryRow("SELECT * FROM training WHERE id = $1", id).Scan(&t.ID, &t.Name, &t.Days, &t.Description, &t.Price)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
func (r *TrainingRepository) Update(t *training.Training) error {
|
||||
_, err := r.db.Exec("UPDATE training SET name = $1, days = $2, description = $3, price = $4 WHERE id = $5", t.Name, t.Days, t.Description, t.Price, t.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *TrainingRepository) Delete(id training.ID) error {
|
||||
_, err := r.db.Exec("DELETE FROM training WHERE id = $1", id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *TrainingRepository) FindAll() ([]training.Training, error) {
|
||||
rows, err := r.db.Query("SELECT * FROM training")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var trainings []training.Training
|
||||
for rows.Next() {
|
||||
var t training.Training
|
||||
err := rows.Scan(&t.ID, &t.Name, &t.Days, &t.Description, &t.Price)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
trainings = append(trainings, t)
|
||||
}
|
||||
|
||||
return trainings, nil
|
||||
}
|
||||
7
internal/server/config.go
Normal file
7
internal/server/config.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package server
|
||||
|
||||
type Config struct {
|
||||
PostgresURL string
|
||||
Port int
|
||||
ShutdownGraceSeconds int
|
||||
}
|
||||
145
internal/server/server.go
Normal file
145
internal/server/server.go
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
fiberzap "github.com/gofiber/contrib/fiberzap/v2"
|
||||
"github.com/gofiber/contrib/swagger"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
middleware "github.com/oapi-codegen/fiber-middleware"
|
||||
"gitlab.mareshq.com/hq/backoffice/backoffice-api/internal/api"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
ready ready
|
||||
}
|
||||
|
||||
func NewServer() *Server {
|
||||
return &Server{}
|
||||
}
|
||||
|
||||
func (s *Server) Run() {
|
||||
shutdownCtx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
logger, err := zap.NewProduction()
|
||||
if err != nil {
|
||||
logger.Fatal("failed to initialize logger", zap.Error(err))
|
||||
}
|
||||
defer logger.Sync()
|
||||
sugaredLogger := logger.Sugar()
|
||||
|
||||
fiberConfig := fiber.Config{
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 5 * time.Second,
|
||||
IdleTimeout: 5 * time.Second,
|
||||
|
||||
DisableStartupMessage: true,
|
||||
}
|
||||
|
||||
app := fiber.New(fiberConfig)
|
||||
|
||||
app.Get("/", func(c *fiber.Ctx) error {
|
||||
return c.SendStatus(fiber.StatusNotFound)
|
||||
})
|
||||
|
||||
app.Get("/livez", func(c *fiber.Ctx) error {
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
})
|
||||
|
||||
app.Get("/readyz", func(c *fiber.Ctx) error {
|
||||
if s.ready.isReady() {
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
|
||||
return c.SendStatus(fiber.StatusServiceUnavailable)
|
||||
})
|
||||
|
||||
// TODO: add /metrics endpoint
|
||||
|
||||
app.Use(fiberzap.New(fiberzap.Config{
|
||||
Logger: logger,
|
||||
}))
|
||||
|
||||
swaggerConfig := swagger.Config{
|
||||
BasePath: "/",
|
||||
FilePath: "./api/v1/openapi.yaml",
|
||||
Path: "/swagger",
|
||||
Title: "Swagger API Docs",
|
||||
}
|
||||
|
||||
app.Use(swagger.New(swaggerConfig))
|
||||
|
||||
app = registerAPIHandlers(app, sugaredLogger)
|
||||
|
||||
<-shutdownCtx.Done()
|
||||
stop()
|
||||
s.ready.notReady()
|
||||
|
||||
// wait till Pod is signaling not ready (no new requests)
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
shutdownBegan := time.Now()
|
||||
|
||||
sugaredLogger.Info("Shutdown signal recieved. Shutting down gracefully...")
|
||||
|
||||
// * Gracefully shut down fiber server
|
||||
// * If does not shutdown within `gracefulShutdownPeriod`, force shutdown
|
||||
// ! Does not close keep-alive connections
|
||||
// ! fiber.Config{ReadTimeout} must be set and non-zero and greater than the `gracefulShutdownPeriod`
|
||||
// TODO: make shutdown timeout configurable
|
||||
err = app.ShutdownWithTimeout(time.Second * 10)
|
||||
if err != nil {
|
||||
sugaredLogger.Errorw("Error during shutdown", "error", err)
|
||||
}
|
||||
|
||||
sugaredLogger.Infof("Shutdown completed in %v", time.Since(shutdownBegan))
|
||||
}
|
||||
|
||||
func registerAPIHandlers(fiberApp *fiber.App, logger *zap.SugaredLogger) *fiber.App {
|
||||
swagger, err := api.GetSwagger()
|
||||
if err != nil {
|
||||
logger.Fatalf("Error getting swagger: %v", err)
|
||||
}
|
||||
|
||||
// TODO: validate this. Copied from example
|
||||
// See: https://github.com/deepmap/oapi-codegen/blob/master/examples/petstore-expanded/fiber/petstore.go#L41
|
||||
// Clear out the servers array in the swagger spec, that skips validating
|
||||
// that server names match. We don't know how this thing will be run.
|
||||
swagger.Servers = nil
|
||||
|
||||
fiberApp.Use(middleware.OapiRequestValidator(swagger))
|
||||
|
||||
api.RegisterHandlers(fiberApp)
|
||||
|
||||
return fiberApp
|
||||
}
|
||||
|
||||
type ready struct {
|
||||
ready bool
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func (r *ready) setReady() {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
r.ready = true
|
||||
}
|
||||
|
||||
func (r *ready) isReady() bool {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
return r.ready
|
||||
}
|
||||
|
||||
func (r *ready) notReady() {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
r.ready = false
|
||||
}
|
||||
6
internal/version/version.go
Normal file
6
internal/version/version.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package version
|
||||
|
||||
var (
|
||||
Version = "dev"
|
||||
Commit = "N/A"
|
||||
)
|
||||
|
|
@ -3,5 +3,6 @@ package: api
|
|||
generate:
|
||||
models: true
|
||||
fiber-server: true
|
||||
strict-server: true
|
||||
embedded-spec: true
|
||||
output: ./internal/api/api.gen.go
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ import "github.com/google/uuid"
|
|||
|
||||
type AttendeeID uuid.UUID
|
||||
|
||||
func NewAttendeeID() AttendeeID {
|
||||
id := uuid.Must(uuid.NewV7())
|
||||
return AttendeeID(id)
|
||||
}
|
||||
|
||||
type Attendee struct {
|
||||
ID AttendeeID
|
||||
DateID DateID
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ type AttendeeRepository interface {
|
|||
FindAll() ([]Attendee, error)
|
||||
FindAllForDate(DateID) ([]Attendee, error)
|
||||
CountForDate(DateID) (int, error)
|
||||
Save(*Attendee) error
|
||||
Create(*Attendee) error
|
||||
Update(*Attendee) error
|
||||
UpdateAttendance(AttendeeID, bool) error
|
||||
UpdatePayment(AttendeeID, bool) error
|
||||
|
|
|
|||
|
|
@ -8,15 +8,21 @@ import (
|
|||
|
||||
type DateID uuid.UUID
|
||||
|
||||
func NewDateID() DateID {
|
||||
id := uuid.Must(uuid.NewV7())
|
||||
return DateID(id)
|
||||
}
|
||||
|
||||
type Date struct {
|
||||
ID DateID
|
||||
TrainingID ID
|
||||
Date time.Time
|
||||
StartTime time.Time
|
||||
Days int8
|
||||
Price Price
|
||||
IsOnline bool
|
||||
Location string // could be empty (null) for example: Prague, Brno, London, ...
|
||||
Address string // could be empty (null)
|
||||
Capacity int8
|
||||
PriceAmount float64
|
||||
PriceCurrency string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ type DateRepository interface {
|
|||
Get(DateID) (*Date, error)
|
||||
FindAll() ([]Date, error)
|
||||
FindAllForTraining(ID) ([]Date, error)
|
||||
Save(*Date) error
|
||||
Create(*Date) error
|
||||
Update(*Date) error
|
||||
Delete(DateID) error
|
||||
IsFull(DateID) (bool, error)
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
package training
|
||||
|
||||
import "github.com/google/uuid"
|
||||
|
||||
type FeedbackID uuid.UUID
|
||||
|
||||
type Feedback struct {
|
||||
ID FeedbackID
|
||||
AttendeeID AttendeeID
|
||||
Rating int
|
||||
Comment string
|
||||
IsAnonymous bool
|
||||
IsSharingAllowed bool
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
package training
|
||||
|
||||
type FeedbackRepository interface {
|
||||
Get(FeedbackID) (*Feedback, error)
|
||||
FindAll() ([]Feedback, error)
|
||||
Save(*Feedback) error
|
||||
Update(*Feedback) error
|
||||
Delete(FeedbackID) error
|
||||
}
|
||||
|
|
@ -1,3 +1,22 @@
|
|||
package training
|
||||
|
||||
type Price float32
|
||||
// type Price float32
|
||||
|
||||
type TrainingPrice struct {
|
||||
Currency string `json:"currency"`
|
||||
Amount float64 `json:"amount"`
|
||||
Type PriceType `json:"type"` // open | corporate
|
||||
}
|
||||
|
||||
type PriceType string
|
||||
|
||||
var (
|
||||
PriceTypes = []PriceType{OpenPrice, CorporatePrice, StudentPrice, GovernmentPrice}
|
||||
)
|
||||
|
||||
const (
|
||||
OpenPrice PriceType = "OPEN"
|
||||
CorporatePrice PriceType = "CORPORATE"
|
||||
StudentPrice PriceType = "STUDENT"
|
||||
GovernmentPrice PriceType = "GOVERNMENT"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package training
|
||||
|
||||
type Repository interface {
|
||||
Save(*Training) error
|
||||
Create(*Training) error
|
||||
Get(ID) (*Training, error)
|
||||
Update(*Training) error
|
||||
Delete(ID) error
|
||||
|
|
|
|||
|
|
@ -6,10 +6,15 @@ import (
|
|||
|
||||
type ID uuid.UUID
|
||||
|
||||
func NewID() ID {
|
||||
id := uuid.Must(uuid.NewV7())
|
||||
return ID(id)
|
||||
}
|
||||
|
||||
type Training struct {
|
||||
ID ID
|
||||
Days int8
|
||||
Name string
|
||||
Description string
|
||||
Price Price
|
||||
Price []TrainingPrice
|
||||
}
|
||||
|
|
|
|||
25
redocly.yaml
Normal file
25
redocly.yaml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# See https://redocly.com/docs/cli/configuration/
|
||||
|
||||
extends:
|
||||
- recommended
|
||||
|
||||
apis:
|
||||
backoffice@v1:
|
||||
root: ./api/v1/openapi.yaml
|
||||
rules:
|
||||
no-ambiguous-paths: error
|
||||
|
||||
rules:
|
||||
no-unused-components: error
|
||||
operation-singular-tag: warn
|
||||
boolean-parameter-prefixes:
|
||||
severity: error
|
||||
prefixes: ["can", "is", "has"]
|
||||
|
||||
theme:
|
||||
openapi:
|
||||
generateCodeSamples:
|
||||
languages:
|
||||
- lang: curl
|
||||
- lang: JavaScript
|
||||
- lang: Go
|
||||
Reference in a new issue