diff --git a/docs/spec/components/schemas/Template.yaml b/docs/spec/components/schemas/Template.yaml index d270938c..769dcefb 100644 --- a/docs/spec/components/schemas/Template.yaml +++ b/docs/spec/components/schemas/Template.yaml @@ -21,6 +21,8 @@ allOf: - is_completed - template_name - template_short_name + - template_id + - is_default_template properties: template: type: object @@ -33,4 +35,9 @@ allOf: type: string template_short_name: type: string + template_id: + type: int + format: int64 + is_default_template: + type: boolean diff --git a/internal/assets/migrations/002_default_template.sql b/internal/assets/migrations/002_default_template.sql new file mode 100644 index 00000000..ffb6d7d4 --- /dev/null +++ b/internal/assets/migrations/002_default_template.sql @@ -0,0 +1,9 @@ +-- +migrate Up +ALTER TABLE template DROP COLUMN is_default_template; + +ALTER TABLE template ADD is_default_template BOOLEAN DEFAULT False; + + +-- +migrate Down + +ALTER TABLE template DROP COLUMN is_default_template; diff --git a/internal/data/pg/template.go b/internal/data/pg/template.go index efe12c8b..04a0ef35 100644 --- a/internal/data/pg/template.go +++ b/internal/data/pg/template.go @@ -21,6 +21,7 @@ func NewTemplateQ(db *pgdb.DB) data.TemplateQ { db: db, sql: sq.Select("b.*").From(fmt.Sprintf("%s as b", templateTableName)), upd: sq.Update(templateTableName), + dlt: sq.Delete(templateTableName), } } @@ -28,6 +29,7 @@ type TemplateQ struct { db *pgdb.DB sql sq.SelectBuilder upd sq.UpdateBuilder + dlt sq.DeleteBuilder } func (q *TemplateQ) New() data.TemplateQ { @@ -79,6 +81,15 @@ func (q *TemplateQ) Insert(value *data.Template) error { return nil } +func (q *TemplateQ) Delete() error { + err := q.db.Exec(q.dlt) + if err != nil { + return errors.Wrap(err, "failed to delete template") + } + + return nil +} + func (q *TemplateQ) Page(pageParams pgdb.OffsetPageParams) data.TemplateQ { q.sql = pageParams.ApplyTo(q.sql, idField) @@ -87,6 +98,8 @@ func (q *TemplateQ) Page(pageParams pgdb.OffsetPageParams) data.TemplateQ { func (q *TemplateQ) FilterByUser(id int64) data.TemplateQ { q.sql = q.sql.Where(sq.Eq{userIDField: id}) + q.upd = q.upd.Where(sq.Eq{userIDField: id}) + q.dlt = q.dlt.Where(sq.Eq{userIDField: id}) return q } @@ -94,6 +107,7 @@ func (q *TemplateQ) FilterByUser(id int64) data.TemplateQ { func (q *TemplateQ) FilterByID(id int64) data.TemplateQ { q.sql = q.sql.Where(sq.Eq{idField: id}) q.upd = q.upd.Where(sq.Eq{idField: id}) + q.dlt = q.dlt.Where(sq.Eq{idField: id}) return q } @@ -101,6 +115,7 @@ func (q *TemplateQ) FilterByID(id int64) data.TemplateQ { func (q *TemplateQ) FilterByName(name string) data.TemplateQ { q.sql = q.sql.Where(sq.Eq{nameField: name}) q.upd = q.upd.Where(sq.Eq{nameField: name}) + q.dlt = q.dlt.Where(sq.Eq{nameField: name}) return q } @@ -108,6 +123,7 @@ func (q *TemplateQ) FilterByName(name string) data.TemplateQ { func (q *TemplateQ) FilterByShortName(name string) data.TemplateQ { q.sql = q.sql.Where(sq.Eq{nameField: name}) q.upd = q.upd.Where(sq.Eq{nameField: name}) + q.dlt = q.dlt.Where(sq.Eq{nameField: name}) return q } diff --git a/internal/data/template.go b/internal/data/template.go index 131f3469..4e25fed9 100644 --- a/internal/data/template.go +++ b/internal/data/template.go @@ -5,6 +5,7 @@ type TemplateQ interface { Get() (*Template, error) Insert(data *Template) error Update(data *Template) error + Delete() error Select() ([]Template, error) FilterByUser(ids int64) TemplateQ FilterByName(name string) TemplateQ @@ -13,10 +14,11 @@ type TemplateQ interface { } type Template struct { - ID int64 `db:"id" structs:"-"` - UserID int64 `db:"user_id" structs:"user_id"` - Name string `db:"name" structs:"name"` - ShortName string `db:"short_name" structs:"short_name"` - Template []byte `db:"template" structs:"template"` - ImgBytes []byte `db:"img_bytes" structs:"img_bytes"` //todo make better + ID int64 `db:"id" structs:"-"` + UserID int64 `db:"user_id" structs:"user_id"` + Name string `db:"name" structs:"name"` + ShortName string `db:"short_name" structs:"short_name"` + Template []byte `db:"template" structs:"template"` + ImgBytes []byte `db:"img_bytes" structs:"img_bytes"` //todo make better + IsDefaultTemplate bool `db:"is_default_template" structs:"is_default_template"` } diff --git a/internal/service/api/handlers/create_template.go b/internal/service/api/handlers/create_template.go index bd60ca6b..7a2ad7ac 100644 --- a/internal/service/api/handlers/create_template.go +++ b/internal/service/api/handlers/create_template.go @@ -17,6 +17,7 @@ func CreateTemplate(w http.ResponseWriter, r *http.Request) { ape.RenderErr(w, problems.BadRequest(err)...) return } + client, err := MasterQ(r).ClientQ().FilterByName(req.Data.Relationships.User.Data.ID).Get() if err != nil { Log(r).WithError(err).Error("failed to get client") @@ -38,11 +39,12 @@ func CreateTemplate(w http.ResponseWriter, r *http.Request) { } err = MasterQ(r).TemplateQ().Insert(&data.Template{ - Template: templateBytes, - ImgBytes: []byte(strings.Replace(req.Data.Attributes.BackgroundImg, "data:image/png;base64,", "", 1)), - Name: req.Data.Attributes.TemplateName, - ShortName: req.Data.Attributes.TemplateShortName, - UserID: client.ID, + Template: templateBytes, + ImgBytes: []byte(strings.Replace(req.Data.Attributes.BackgroundImg, "data:image/png;base64,", "", 1)), + Name: req.Data.Attributes.TemplateName, + ShortName: req.Data.Attributes.TemplateShortName, + UserID: client.ID, + IsDefaultTemplate: req.Data.Attributes.IsDefaultTemplate, }) if err != nil { Log(r).WithError(err).Error("failed to insert template") diff --git a/internal/service/api/handlers/get_templates.go b/internal/service/api/handlers/get_templates.go index e8b5aafd..555b08a6 100644 --- a/internal/service/api/handlers/get_templates.go +++ b/internal/service/api/handlers/get_templates.go @@ -36,6 +36,7 @@ func GetTemplates(w http.ResponseWriter, r *http.Request) { ape.RenderErr(w, problems.InternalError()) return } + ape.Render(w, newTemlateListResp(tmps)) } @@ -47,6 +48,7 @@ func newTemlateListResp(tmps []data.Template) resources.TemplateListResponse { Attributes: resources.TemplateAttributes{ BackgroundImg: string(tmp.ImgBytes), TemplateName: tmp.Name, + TemplateId: tmp.ID, }, }) } diff --git a/internal/service/api/handlers/remove_template_by_id.go b/internal/service/api/handlers/remove_template_by_id.go new file mode 100644 index 00000000..467fc676 --- /dev/null +++ b/internal/service/api/handlers/remove_template_by_id.go @@ -0,0 +1,27 @@ +package handlers + +import ( + "gitlab.com/distributed_lab/ape" + "gitlab.com/distributed_lab/ape/problems" + "gitlab.com/tokend/course-certificates/ccp/internal/service/api/requests" + "net/http" +) + +func RemoveTemplateByID(w http.ResponseWriter, r *http.Request) { + templateID, err := requests.NewRemoveTemplateByIDRequest(r) + if err != nil { + Log(r).WithError(err).Error("failed to parse request ") + ape.Render(w, problems.BadRequest(err)) + return + } + + templateID64 := int64(*templateID) + err = MasterQ(r).TemplateQ().FilterByID(templateID64).Delete() + if err != nil { + Log(r).WithError(err).Error("failed to remove template") + ape.Render(w, problems.InternalError()) + return + } + + w.WriteHeader(http.StatusOK) +} diff --git a/internal/service/api/requests/remove_template_by_id.go b/internal/service/api/requests/remove_template_by_id.go new file mode 100644 index 00000000..6b8cffc7 --- /dev/null +++ b/internal/service/api/requests/remove_template_by_id.go @@ -0,0 +1,32 @@ +package requests + +import ( + "github.com/go-chi/chi" + "gitlab.com/distributed_lab/logan/v3/errors" + "gitlab.com/distributed_lab/urlval" + "net/http" + "strconv" +) + +const ( + TemplateIDPathParam = "template_id" +) + +type RemoveTemplateByIDRequest struct { + TemplateID string `url:"-"` +} + +func NewRemoveTemplateByIDRequest(r *http.Request) (*int, error) { + request := RemoveTemplateByIDRequest{} + if err := urlval.Decode(r.URL.Query(), &request); err != nil { + return nil, errors.Wrap(err, "failed to decode data") + } + request.TemplateID = chi.URLParam(r, TemplateIDPathParam) + + id, err := strconv.Atoi(request.TemplateID) + if err != nil { + return nil, errors.Wrap(err, "failed to convert template id to int") + } + + return &id, nil +} diff --git a/internal/service/api/router.go b/internal/service/api/router.go index 82680102..990d4fbd 100644 --- a/internal/service/api/router.go +++ b/internal/service/api/router.go @@ -47,6 +47,7 @@ func (s *service) router(cfg config.Config) chi.Router { r.Route("/template", func(r chi.Router) { r.Post("/", handlers.CreateTemplate) r.Get("/{user}", handlers.GetTemplates) + r.Delete("/{template_id}", handlers.RemoveTemplateByID) r.Get("/{user}/{name}", handlers.GetTemplateByName) }) }) diff --git a/internal/service/core/pdf/container.go b/internal/service/core/pdf/container.go index 10f5238d..7f7febde 100644 --- a/internal/service/core/pdf/container.go +++ b/internal/service/core/pdf/container.go @@ -53,6 +53,7 @@ func (c *Container) Generate() error { files = append(files, google.FilesBytes{File: file, Name: name, ID: user.ID, Type: "image/svg+xml"}) pdf := PDF{} + certificateTemplate, err := pdf.InitTemplate(c.masterQ, user.CourseTitle, c.owner.ID) if err != nil { return errors.Wrap(err, "failed to get template") diff --git a/internal/service/core/pdf/default_data.go b/internal/service/core/pdf/default_data.go index fc53c22f..4863dd9c 100644 --- a/internal/service/core/pdf/default_data.go +++ b/internal/service/core/pdf/default_data.go @@ -7,12 +7,14 @@ var DefaultTemplateNormal = PDF{ X: 200, Y: 217, FontSize: 28, + XCenter: true, Font: "semibold", }, Course: Field{ X: 61, Y: 259, FontSize: 14, + XCenter: true, Font: "semibold", }, Credits: Field{ @@ -55,6 +57,7 @@ var DefaultTemplateNormal = PDF{ X: 300, Y: 277, FontSize: 14, + XCenter: true, Font: "semibold", }, } @@ -65,11 +68,13 @@ var DefaultTemplateTall = PDF{ Name: Field{ Y: 434, FontSize: 56, + XCenter: true, Font: "semibold", }, Course: Field{ Y: 518, FontSize: 28, + XCenter: true, Font: "semibold", }, Credits: Field{ //todo get from front and save to db @@ -108,18 +113,21 @@ var DefaultTemplateTall = PDF{ Exam: Field{ Y: 600, FontSize: 30, + XCenter: true, Font: "italic", }, Level: Field{ Y: 554, FontSize: 28, + XCenter: true, Font: "semibold", }, } var DefaultData = PDFData{ - Name: "Test Name", - Course: "Blockchain and Distributed Systems", + Name: "Test Name", + Course: "Blockchain and Distributed Systems", + Credits: " 99", Points: "100", SerialNumber: "694d0f5a7afe6fbc99cb", diff --git a/internal/service/core/pdf/pdf.go b/internal/service/core/pdf/pdf.go index c5d25dba..4aac8b17 100644 --- a/internal/service/core/pdf/pdf.go +++ b/internal/service/core/pdf/pdf.go @@ -446,7 +446,7 @@ func (p *PDF) InitTemplate(masterQ data.MasterQ, templateName string, userID int if err != nil { return nil, errors.Wrap(err, "failed to get template data") } - if template == nil || template.Template == nil { + if template == nil || template.Template == nil || template.IsDefaultTemplate { return &DefaultTemplateTall, nil } diff --git a/resources/model_template_attributes.go b/resources/model_template_attributes.go index e1fdbe33..4f711fe4 100644 --- a/resources/model_template_attributes.go +++ b/resources/model_template_attributes.go @@ -9,7 +9,9 @@ import "encoding/json" type TemplateAttributes struct { BackgroundImg string `json:"background_img"` IsCompleted bool `json:"is_completed"` + IsDefaultTemplate bool `json:"is_default_template"` Template json.RawMessage `json:"template"` + TemplateId int64 `json:"template_id"` TemplateName string `json:"template_name"` TemplateShortName string `json:"template_short_name"` }