diff --git a/api/projects/schedules.go b/api/projects/schedules.go
new file mode 100644
index 00000000..1a0f45a9
--- /dev/null
+++ b/api/projects/schedules.go
@@ -0,0 +1,149 @@
+package projects
+
+import (
+ log "github.com/Sirupsen/logrus"
+ "github.com/ansible-semaphore/semaphore/api/helpers"
+ "github.com/ansible-semaphore/semaphore/db"
+ "github.com/gorilla/context"
+ "net/http"
+ "strconv"
+)
+
+// SchedulesMiddleware ensures a template exists and loads it to the context
+func SchedulesMiddleware(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ project := context.Get(r, "project").(db.Project)
+ scheduleID, err := helpers.GetIntParam("schedule_id", w, r)
+ if err != nil {
+ return
+ }
+
+ schedule, err := helpers.Store(r).GetSchedule(project.ID, scheduleID)
+
+ if err != nil {
+ helpers.WriteError(w, err)
+ return
+ }
+
+ context.Set(r, "schedule", schedule)
+ next.ServeHTTP(w, r)
+ })
+}
+
+// GetSchedule returns single template by ID
+func GetSchedule(w http.ResponseWriter, r *http.Request) {
+ schedule := context.Get(r, "schedule").(db.Schedule)
+ helpers.WriteJSON(w, http.StatusOK, schedule)
+}
+
+// AddSchedule adds a template to the database
+func AddSchedule(w http.ResponseWriter, r *http.Request) {
+ project := context.Get(r, "project").(db.Project)
+
+ var schedule db.Schedule
+ if !helpers.Bind(w, r, &schedule) {
+ return
+ }
+
+ schedule.ProjectID = project.ID
+ schedule, err := helpers.Store(r).CreateSchedule(schedule)
+
+ if err != nil {
+ helpers.WriteError(w, err)
+ return
+ }
+
+ user := context.Get(r, "user").(*db.User)
+ objType := "schedule"
+ desc := "Schedule ID " + strconv.Itoa(schedule.ID) + " created"
+
+ _, err = helpers.Store(r).CreateEvent(db.Event{
+ UserID: &user.ID,
+ ProjectID: &project.ID,
+ ObjectType: &objType,
+ ObjectID: &schedule.ID,
+ Description: &desc,
+ })
+
+ if err != nil {
+ log.Error(err)
+ }
+
+ helpers.WriteJSON(w, http.StatusCreated, schedule)
+}
+
+// UpdateSchedule writes a schedule to an existing key in the database
+func UpdateSchedule(w http.ResponseWriter, r *http.Request) {
+ oldSchedule := context.Get(r, "schedule").(db.Schedule)
+
+ var schedule db.Schedule
+ if !helpers.Bind(w, r, &schedule) {
+ return
+ }
+
+ // project ID and schedule ID in the body and the path must be the same
+
+ if schedule.ID != oldSchedule.ID {
+ helpers.WriteJSON(w, http.StatusBadRequest, map[string]string{
+ "error": "schedule id in URL and in body must be the same",
+ })
+ return
+ }
+
+ if schedule.ProjectID != oldSchedule.ProjectID {
+ helpers.WriteJSON(w, http.StatusBadRequest, map[string]string{
+ "error": "You can not move schedule to other project",
+ })
+ return
+ }
+
+ err := helpers.Store(r).UpdateSchedule(schedule)
+ if err != nil {
+ helpers.WriteError(w, err)
+ return
+ }
+
+ user := context.Get(r, "user").(*db.User)
+
+ desc := "Schedule ID " + strconv.Itoa(schedule.ID) + " updated"
+ objType := "schedule"
+
+ _, err = helpers.Store(r).CreateEvent(db.Event{
+ UserID: &user.ID,
+ ProjectID: &schedule.ProjectID,
+ Description: &desc,
+ ObjectID: &schedule.ID,
+ ObjectType: &objType,
+ })
+
+ if err != nil {
+ log.Error(err)
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}
+
+// RemoveSchedule deletes a schedule from the database
+func RemoveSchedule(w http.ResponseWriter, r *http.Request) {
+ schedule := context.Get(r, "schedule").(db.Schedule)
+
+ err := helpers.Store(r).DeleteSchedule(schedule.ProjectID, schedule.ID)
+ if err != nil {
+ helpers.WriteError(w, err)
+ return
+ }
+
+ user := context.Get(r, "user").(*db.User)
+ desc := "Schedule ID " + strconv.Itoa(schedule.ID) + " deleted"
+ _, err = helpers.Store(r).CreateEvent(db.Event{
+ UserID: &user.ID,
+ ProjectID: &schedule.ProjectID,
+ Description: &desc,
+ })
+
+ if err != nil {
+ log.Error(err)
+ }
+
+ w.WriteHeader(http.StatusNoContent)
+}
\ No newline at end of file
diff --git a/api/projects/templates.go b/api/projects/templates.go
index a193e690..066d3e84 100644
--- a/api/projects/templates.go
+++ b/api/projects/templates.go
@@ -4,10 +4,9 @@ import (
log "github.com/Sirupsen/logrus"
"github.com/ansible-semaphore/semaphore/api/helpers"
"github.com/ansible-semaphore/semaphore/db"
+ "github.com/gorilla/context"
"net/http"
"strconv"
-
- "github.com/gorilla/context"
)
// TemplatesMiddleware ensures a template exists and loads it to the context
@@ -42,7 +41,7 @@ func GetTemplates(w http.ResponseWriter, r *http.Request) {
project := context.Get(r, "project").(db.Project)
params := db.RetrieveQueryParams{
- SortBy: r.URL.Query().Get("sort"),
+ SortBy: r.URL.Query().Get("sort"),
SortInverted: r.URL.Query().Get("order") == desc,
}
@@ -102,9 +101,17 @@ func UpdateTemplate(w http.ResponseWriter, r *http.Request) {
}
// project ID and template ID in the body and the path must be the same
- if template.ID != oldTemplate.ID || template.ProjectID != oldTemplate.ProjectID {
+
+ if template.ID != oldTemplate.ID {
helpers.WriteJSON(w, http.StatusBadRequest, map[string]string{
- "error": "You can not move ",
+ "error": "template id in URL and in body must be the same",
+ })
+ return
+ }
+
+ if template.ProjectID != oldTemplate.ProjectID {
+ helpers.WriteJSON(w, http.StatusBadRequest, map[string]string{
+ "error": "You can not move template to other project",
})
return
}
@@ -125,7 +132,7 @@ func UpdateTemplate(w http.ResponseWriter, r *http.Request) {
objType := "template"
_, err = helpers.Store(r).CreateEvent(db.Event{
- UserID: &user.ID,
+ UserID: &user.ID,
ProjectID: &template.ProjectID,
Description: &desc,
ObjectID: &template.ID,
@@ -162,4 +169,4 @@ func RemoveTemplate(w http.ResponseWriter, r *http.Request) {
}
w.WriteHeader(http.StatusNoContent)
-}
+}
\ No newline at end of file
diff --git a/api/router.go b/api/router.go
index 976864a6..5c56b204 100644
--- a/api/router.go
+++ b/api/router.go
@@ -189,6 +189,7 @@ func Route() *mux.Router {
projectTmplManagement.HandleFunc("/{template_id}", projects.GetTemplate).Methods("GET")
projectTmplManagement.HandleFunc("/{template_id}/tasks", tasks.GetAllTasks).Methods("GET")
projectTmplManagement.HandleFunc("/{template_id}/tasks/last", tasks.GetLastTasks).Methods("GET")
+ projectTmplManagement.HandleFunc("/{template_id}/schedules", projects.GetSchedule).Methods("GET")
projectTaskManagement := projectUserAPI.PathPrefix("/tasks").Subrouter()
projectTaskManagement.Use(tasks.GetTaskMiddleware)
@@ -198,6 +199,13 @@ func Route() *mux.Router {
projectTaskManagement.HandleFunc("/{task_id}", tasks.RemoveTask).Methods("DELETE")
projectTaskManagement.HandleFunc("/{task_id}/stop", tasks.StopTask).Methods("POST")
+
+ projectScheduleManagement := projectUserAPI.PathPrefix("/schedules").Subrouter()
+ projectScheduleManagement.Use(projects.SchedulesMiddleware)
+ projectScheduleManagement.HandleFunc("/{schedule_id}", projects.GetSchedule).Methods("GET", "HEAD")
+ projectScheduleManagement.HandleFunc("/{schedule_id}", projects.UpdateSchedule).Methods("PUT")
+ projectScheduleManagement.HandleFunc("/{schedule_id}", projects.RemoveSchedule).Methods("DELETE")
+
if os.Getenv("DEBUG") == "1" {
defer debugPrintRoutes(r)
}
diff --git a/api/schedules/pool.go b/api/schedules/pool.go
new file mode 100644
index 00000000..ee705b8e
--- /dev/null
+++ b/api/schedules/pool.go
@@ -0,0 +1,63 @@
+package schedules
+
+import (
+ "github.com/ansible-semaphore/semaphore/db"
+ "github.com/robfig/cron/v3"
+)
+
+type templateRunner struct {
+ schedule db.Schedule
+}
+
+func (r templateRunner) Run() {
+ // TODO: add task to tasks pool
+}
+
+type schedulePool struct {
+ cron *cron.Cron
+ jobs []cron.EntryID
+}
+
+func (p *schedulePool) init() {
+ p.cron = cron.New()
+}
+
+func (p *schedulePool) loadData(d db.Store) {
+ schedules, err := d.GetSchedules()
+
+ if err != nil {
+ // TODO: log error
+ return
+ }
+
+ for _, schedule := range schedules {
+ err := p.addSchedule(schedule)
+ if err != nil {
+ // TODO: log error
+ }
+ }
+}
+
+func (p *schedulePool) addSchedule(schedule db.Schedule) error {
+ id, err := p.cron.AddJob(schedule.CronFormat, templateRunner{
+ schedule: schedule,
+ })
+ if err != nil {
+ return err
+ }
+ p.jobs = append(p.jobs, id)
+ return nil
+}
+
+func (p *schedulePool) run() {
+ p.cron.Run()
+}
+
+var pool = schedulePool{}
+
+// StartRunner begins the schedule pool, used as a goroutine
+func StartRunner(d db.Store) {
+ pool.init()
+ pool.loadData(d)
+ pool.run()
+}
diff --git a/api/tasks/runner.go b/api/tasks/runner.go
index c1f9b49b..4e52c315 100644
--- a/api/tasks/runner.go
+++ b/api/tasks/runner.go
@@ -4,7 +4,11 @@ import (
"bytes"
"encoding/json"
"fmt"
+ log "github.com/Sirupsen/logrus"
+ "github.com/ansible-semaphore/semaphore/api/helpers"
"github.com/ansible-semaphore/semaphore/api/sockets"
+ "github.com/ansible-semaphore/semaphore/db"
+ "github.com/ansible-semaphore/semaphore/util"
"io/ioutil"
"os"
"os/exec"
@@ -12,13 +16,6 @@ import (
"strconv"
"strings"
"time"
-
- "github.com/ansible-semaphore/semaphore/api/helpers"
- "github.com/ansible-semaphore/semaphore/db"
-
- log "github.com/Sirupsen/logrus"
-
- "github.com/ansible-semaphore/semaphore/util"
)
const (
@@ -32,19 +29,19 @@ const (
)
type task struct {
- store db.Store
- task db.Task
- template db.Template
- inventory db.Inventory
- repository db.Repository
- environment db.Environment
- users []int
- projectID int
- hosts []string
- alertChat string
- alert bool
- prepared bool
- process *os.Process
+ store db.Store
+ task db.Task
+ template db.Template
+ inventory db.Inventory
+ repository db.Repository
+ environment db.Environment
+ users []int
+ projectID int
+ hosts []string
+ alertChat string
+ alert bool
+ prepared bool
+ process *os.Process
}
func (t *task) getRepoName() string {
diff --git a/cli/cmd/root.go b/cli/cmd/root.go
index 3e2df162..09f586a3 100644
--- a/cli/cmd/root.go
+++ b/cli/cmd/root.go
@@ -4,6 +4,7 @@ import (
"fmt"
log "github.com/Sirupsen/logrus"
"github.com/ansible-semaphore/semaphore/api"
+ "github.com/ansible-semaphore/semaphore/api/schedules"
"github.com/ansible-semaphore/semaphore/api/sockets"
"github.com/ansible-semaphore/semaphore/api/tasks"
"github.com/ansible-semaphore/semaphore/db"
@@ -74,6 +75,7 @@ func runService() {
go sockets.StartWS()
go tasks.StartRunner()
+ go schedules.StartRunner(store)
route := api.Route()
diff --git a/db/Schedule.go b/db/Schedule.go
new file mode 100644
index 00000000..0c6054f9
--- /dev/null
+++ b/db/Schedule.go
@@ -0,0 +1,8 @@
+package db
+
+type Schedule struct {
+ ID int `db:"id" json:"id"`
+ ProjectID int `db:"project_id" json:"project_id"`
+ TemplateID int `db:"template_id" json:"template_id"`
+ CronFormat string `db:"cron_format" json:"cron_format"`
+}
diff --git a/db/Store.go b/db/Store.go
index c6ab1618..e082493b 100644
--- a/db/Store.go
+++ b/db/Store.go
@@ -43,6 +43,8 @@ func ValidateUsername(login string) error {
return nil
}
+type Transaction interface {}
+
type Store interface {
Connect() error
Close() error
@@ -107,6 +109,13 @@ type Store interface {
GetTemplate(projectID int, templateID int) (Template, error)
DeleteTemplate(projectID int, templateID int) error
+ GetSchedules() ([]Schedule, error)
+ GetTemplateSchedules(projectID int, templateID int) ([]Schedule, error)
+ CreateSchedule(schedule Schedule) (Schedule, error)
+ UpdateSchedule(schedule Schedule) error
+ GetSchedule(projectID int, scheduleID int) (Schedule, error)
+ DeleteSchedule(projectID int, scheduleID int) error
+
GetProjectUsers(projectID int, params RetrieveQueryParams) ([]User, error)
CreateProjectUser(projectUser ProjectUser) (ProjectUser, error)
DeleteProjectUser(projectID int, userID int) error
@@ -274,6 +283,11 @@ var TemplateProps = ObjectProperties{
PrimaryColumnName: "id",
}
+var ScheduleProps = ObjectProperties{
+ TableName: "project__schedule",
+ PrimaryColumnName: "id",
+}
+
var ProjectUserProps = ObjectProperties{
TableName: "project__user",
PrimaryColumnName: "user_id",
@@ -310,5 +324,4 @@ var TaskProps = ObjectProperties{
var TaskOutputProps = ObjectProperties{
TableName: "task__output",
- PrimaryColumnName: "",
}
diff --git a/db/Template.go b/db/Template.go
index c7dc7a1c..c129414c 100644
--- a/db/Template.go
+++ b/db/Template.go
@@ -24,4 +24,4 @@ type Template struct {
VaultPassID *int `db:"vault_pass_id" json:"vault_pass_id"`
VaultPass AccessKey `db:"-" json:"-"`
-}
+}
\ No newline at end of file
diff --git a/db/bolt/schedule.go b/db/bolt/schedule.go
new file mode 100644
index 00000000..3aee2258
--- /dev/null
+++ b/db/bolt/schedule.go
@@ -0,0 +1,48 @@
+package bolt
+
+import "github.com/ansible-semaphore/semaphore/db"
+
+func (d *BoltDb) GetSchedules() (schedules []db.Schedule, err error) {
+ var allProjects []db.Project
+
+ err = d.getObjects(0, db.ProjectProps, db.RetrieveQueryParams{}, nil, &allProjects)
+
+ if err != nil {
+ return
+ }
+
+ for _, proj := range allProjects {
+ var projSchedules []db.Schedule
+ projSchedules, err = d.GetProjectSchedules(proj.ID)
+ if err != nil {
+ return
+ }
+ schedules = append(schedules, projSchedules...)
+ }
+
+ return
+}
+
+func (d *BoltDb) GetProjectSchedules(projectID int) (schedules []db.Schedule, err error) {
+ err = d.getObjects(projectID, db.ScheduleProps, db.RetrieveQueryParams{}, nil, &schedules)
+ return
+}
+
+
+func (d *BoltDb) GetTemplateSchedules(projectID int, templateID int) (schedule db.Schedule, err error) {
+ projSchedules, err := d.GetProjectSchedules(projectID)
+ if err != nil {
+ return
+ }
+
+ for _, s := range projSchedules {
+ if s.TemplateID == templateID {
+ schedule = s
+ return
+ }
+ }
+
+ err = db.ErrNotFound
+ return
+}
+
diff --git a/db/sql/Version.go b/db/sql/Version.go
index 31f1f0ec..692554bf 100644
--- a/db/sql/Version.go
+++ b/db/sql/Version.go
@@ -81,5 +81,6 @@ func init() {
{Major: 2, Minor: 7, Patch: 9},
{Major: 2, Minor: 7, Patch: 10},
{Major: 2, Minor: 7, Patch: 12},
+ {Major: 2, Minor: 7, Patch: 13},
}
}
diff --git a/db/sql/migration.go b/db/sql/migration.go
index fc73bbb7..297ca1c0 100644
--- a/db/sql/migration.go
+++ b/db/sql/migration.go
@@ -71,7 +71,8 @@ func (d *SqlDb) applyMigration(version *Version) error {
}
q := d.prepareMigration(query)
- if _, err := tx.Exec(q); err != nil {
+ _, err = tx.Exec(q)
+ if err != nil {
handleRollbackError(tx.Rollback())
log.Warnf("\n ERR! Query: %v\n\n", q)
return err
diff --git a/db/sql/migrations/v2.7.13.sql b/db/sql/migrations/v2.7.13.sql
new file mode 100644
index 00000000..f6aa247f
--- /dev/null
+++ b/db/sql/migrations/v2.7.13.sql
@@ -0,0 +1,3 @@
+alter table project__template_schedule rename to project__schedule;
+alter table `project__schedule` add `id` integer primary key autoincrement;
+alter table `project__schedule` add `project_id` int not null references project(`id`);
diff --git a/db/sql/schedule.go b/db/sql/schedule.go
new file mode 100644
index 00000000..3a864393
--- /dev/null
+++ b/db/sql/schedule.go
@@ -0,0 +1,67 @@
+package sql
+
+import (
+ "database/sql"
+ "github.com/ansible-semaphore/semaphore/db"
+)
+
+func (d *SqlDb) CreateSchedule(schedule db.Schedule) (newSchedule db.Schedule, err error) {
+ insertID, err := d.insert(
+ "id",
+ "insert into project__schedule (project_id, template_id, cron_format)" +
+ "values (?, ?, ?)",
+ schedule.ProjectID,
+ schedule.TemplateID,
+ schedule.CronFormat)
+
+ if err != nil {
+ return
+ }
+
+ newSchedule = schedule
+ newSchedule.ID = insertID
+
+ return
+}
+
+func (d *SqlDb) UpdateSchedule(schedule db.Schedule) error {
+ _, err := d.exec("update project__schedule set cron_format=? where project_id=? and id=?",
+ schedule.CronFormat,
+ schedule.ProjectID,
+ schedule.ID)
+
+ return err
+}
+
+func (d *SqlDb) GetSchedule(projectID int, scheduleID int) (template db.Schedule, err error) {
+ err = d.selectOne(
+ &template,
+ "select * from project__schedule where project_id=? and id=?",
+ projectID,
+ scheduleID)
+
+ if err == sql.ErrNoRows {
+ err = db.ErrNotFound
+ }
+
+ return
+}
+
+func (d *SqlDb) DeleteSchedule(projectID int, scheduleID int) error {
+ _, err := d.exec("delete project__schedule where project_id=? and id=?", projectID, scheduleID)
+
+ return err
+}
+
+func (d *SqlDb) GetSchedules() (schedules []db.Schedule, err error) {
+ _, err = d.selectAll(&schedules, "select * from project__template_schedule where cron_format != ''")
+ return
+}
+
+func (d *SqlDb) GetTemplateSchedules(projectID int, templateID int) (schedules []db.Schedule, err error) {
+ _, err = d.selectAll(&schedules,
+ "select * from project__template_schedule where project_id=? and template_id=?",
+ projectID,
+ templateID)
+ return
+}
diff --git a/db/sql/template.go b/db/sql/template.go
index 4ee5050c..bbacf78a 100644
--- a/db/sql/template.go
+++ b/db/sql/template.go
@@ -24,15 +24,21 @@ func (d *SqlDb) CreateTemplate(template db.Template) (newTemplate db.Template, e
return
}
+ err = db.FillTemplate(d, &newTemplate)
+
+ if err != nil {
+ return
+ }
+
newTemplate = template
newTemplate.ID = insertID
- err = db.FillTemplate(d, &newTemplate)
+
return
}
func (d *SqlDb) UpdateTemplate(template db.Template) error {
_, err := d.exec("update project__template set inventory_id=?, repository_id=?, environment_id=?, alias=?, " +
- "playbook=?, arguments=?, override_args=? where removed = false and id=?",
+ "playbook=?, arguments=?, override_args=? where removed = false and id=? and project_id=?",
template.InventoryID,
template.RepositoryID,
template.EnvironmentID,
@@ -40,8 +46,33 @@ func (d *SqlDb) UpdateTemplate(template db.Template) error {
template.Playbook,
template.Arguments,
template.OverrideArguments,
- template.ID)
-
+ template.ID,
+ template.ProjectID)
+
+ //if err != nil {
+ // return err
+ //}
+ //
+ //if template.CronFormat == "" {
+ // _, err = d.exec(
+ // "delete from project__template_schedule where project_id =? and template_id=?",
+ // template.ProjectID,
+ // template.ID)
+ //} else {
+ // _, err = d.GetTemplateSchedules(template.ProjectID, template.ID)
+ // if err == nil {
+ // _, err = d.exec(
+ // "update project__template_schedule set cron_format=? where project_id =? and template_id=?",
+ // template.CronFormat,
+ // template.ProjectID,
+ // template.ID)
+ // } else if err == db.ErrNotFound {
+ // _, err = d.exec(
+ // "insert into project__template_schedule (template_id, cron_format) values (?, ?)",
+ // template.ID,
+ // template.CronFormat)
+ // }
+ //}
return err
}
@@ -117,11 +148,4 @@ func (d *SqlDb) DeleteTemplate(projectID int, templateID int) error {
_, err := d.exec("update project__template set removed=true where project_id=? and id=?", projectID, templateID)
return err
-
- //res, err := d.exec(
- // "delete from project__template where project_id=? and id=?",
- // projectID,
- // templateID)
-
- //return validateMutationResult(res, err)
}
diff --git a/go.mod b/go.mod
index 3a01d7dd..f9b31cf2 100644
--- a/go.mod
+++ b/go.mod
@@ -40,6 +40,7 @@ require (
github.com/onsi/ginkgo v1.12.0 // indirect
github.com/onsi/gomega v1.9.0 // indirect
github.com/radovskyb/watcher v1.0.7 // indirect
+ github.com/robfig/cron/v3 v3.0.1
github.com/russross/blackfriday v1.5.2 // indirect
github.com/sirupsen/logrus v1.4.2 // indirect
github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa
diff --git a/go.sum b/go.sum
index 01c71b6a..0f4aeb29 100644
--- a/go.sum
+++ b/go.sum
@@ -379,6 +379,8 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
+github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
+github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
diff --git a/web2/src/App.vue b/web2/src/App.vue
index 1fef8cd8..0f775c93 100644
--- a/web2/src/App.vue
+++ b/web2/src/App.vue
@@ -762,11 +762,11 @@ export default {
if (!this.isAuthenticated) {
return;
}
- this.user = (await axios({
- method: 'get',
- url: '/api/user',
- responseType: 'json',
- })).data;
+ this.user = (await axios({
+ method: 'get',
+ url: '/api/user',
+ responseType: 'json',
+ })).data;
},
getProjectColor(projectData) {
diff --git a/web2/src/components/ItemFormBase.js b/web2/src/components/ItemFormBase.js
index 3b9ed6e2..27b444c2 100644
--- a/web2/src/components/ItemFormBase.js
+++ b/web2/src/components/ItemFormBase.js
@@ -79,6 +79,10 @@ export default {
throw new Error('Not implemented'); // must me implemented in template
},
+ afterSave() {
+
+ },
+
getNewItem() {
return {};
},
@@ -142,6 +146,8 @@ export default {
...(this.getRequestOptions()),
})).data;
+ await this.afterSave();
+
this.$emit('save', {
item: item || this.item,
action: this.isNew ? 'new' : 'edit',
diff --git a/web2/src/components/TemplateForm.vue b/web2/src/components/TemplateForm.vue
index 8434c79a..74dadfe7 100644
--- a/web2/src/components/TemplateForm.vue
+++ b/web2/src/components/TemplateForm.vue
@@ -67,6 +67,13 @@
:disabled="formSaving"
>
+
@@ -134,6 +141,8 @@ export default {
inventory: null,
repositories: null,
environment: null,
+ schedules: null,
+ cronFormat: null,
};
},
@@ -177,6 +186,14 @@ export default {
url: `/api/project/${this.projectId}/environment`,
responseType: 'json',
})).data;
+ this.schedules = (await axios({
+ keys: 'get',
+ url: `/api/project/${this.projectId}/templates/${this.sourceItemId}/schedules`,
+ responseType: 'json',
+ })).data;
+ if (this.schedules.length === 1) {
+ this.cronFormat = this.schedules[0].cron_format;
+ }
},
computed: {
@@ -189,7 +206,8 @@ export default {
&& this.repositories != null
&& this.inventory != null
&& this.environment != null
- && this.item != null;
+ && this.item != null
+ && this.schedules != null;
},
loginPasswordKeys() {
@@ -208,6 +226,45 @@ export default {
getSingleItemUrl() {
return `/api/project/${this.projectId}/templates/${this.itemId}`;
},
+
+ async afterSave(newItem) {
+ if (newItem || this.schedules.length === 0) {
+ if (this.cronFormat !== '') {
+ // new schedule
+ await axios({
+ method: 'post',
+ url: `/api/project/${this.projectId}/schedules`,
+ responseType: 'json',
+ data: {
+ project_id: this.projectId,
+ template_id: newItem ? newItem.id : this.itemId,
+ cron_format: this.cronFormat,
+ },
+ });
+ }
+ } else if (this.schedules.length > 1) {
+ // do nothing
+ } else if (this.schedules === '') {
+ // drop schedule
+ await axios({
+ method: 'delete',
+ url: `/api/project/${this.projectId}/schedules/${this.schedules[0].id}`,
+ responseType: 'json',
+ });
+ } else {
+ // update schedule
+ await axios({
+ method: 'put',
+ url: `/api/project/${this.projectId}/schedules/${this.schedules[0].id}`,
+ responseType: 'json',
+ data: {
+ project_id: this.projectId,
+ template_id: this.itemId,
+ cron_format: this.cronFormat,
+ },
+ });
+ }
+ },
},
};