# HG changeset patch # User Thomas Junk # Date 1543913731 -3600 # Node ID 5ad79db38ecf0bae1b24cfcecaef1b26f53f1e10 # Parent ccf9fd7ff0bd5ee265e265d2ce281af251a22939# Parent d7152eb11d58568f2926510cac60427dbff8095b merge with default diff -r d7152eb11d58 -r 5ad79db38ecf client/src/components/map/contextbox/Staging.vue --- a/client/src/components/map/contextbox/Staging.vue Mon Dec 03 20:46:35 2018 +0100 +++ b/client/src/components/map/contextbox/Staging.vue Tue Dec 04 09:55:31 2018 +0100 @@ -85,6 +85,7 @@ * Markus Kottländer */ import { mapState } from "vuex"; +import { HTTP } from "../../../lib/http.js"; import { STATES } from "../../../store/imports.js"; import { displayError, displayInfo } from "../../../lib/errors.js"; @@ -93,13 +94,7 @@ return {}; }, mounted() { - this.$store.dispatch("imports/getStaging").catch(error => { - const { status, data } = error.response; - displayError({ - title: this.$gettext("Backend Error"), - message: `${status}: ${data.message || data}` - }); - }); + this.loadData(); }, computed: { ...mapState("application", ["searchQuery"]), @@ -115,16 +110,55 @@ }, STATES: STATES, methods: { + loadData() { + this.$store.dispatch("imports/getStaging").catch(error => { + const { status, data } = error.response; + displayError({ + title: "Backend Error", + message: `${status}: ${data.message || data}` + }); + }); + }, confirmReview() { - const message = this.staging - .map(x => { - return x.id + ": " + x.status; + const reviewResults = this.staging + .filter(x => x.status !== STATES.NEEDSAPPROVAL) + .map(r => { + return { + id: r.id, + state: r.status + }; + }); + if (!reviewResults.length) return; + HTTP.patch("/imports", reviewResults, { + headers: { + "X-Gemma-Auth": localStorage.getItem("token"), + "Content-type": "application/json" + } + }) + .then(response => { + const messages = response.data + .map(x => { + if (x.message) return x.message; + if (x.error) return x.error; + }) + .join("\n\n"); + displayInfo({ + title: "Staging Area", + message: messages, + options: { + timeout: 0, + buttons: [{ text: "Ok", action: null, bold: true }] + } + }); + this.loadData(); }) - .join("\n"); - displayInfo({ - title: this.$gettext("Staging Area"), - message: message - }); + .catch(error => { + const { status, data } = error.response; + displayError({ + title: "Backend Error", + message: `${status}: ${data.message || data}` + }); + }); }, needsApproval(item) { return item.status === STATES.NEEDSAPPROVAL; diff -r d7152eb11d58 -r 5ad79db38ecf client/src/lib/errors.js --- a/client/src/lib/errors.js Mon Dec 03 20:46:35 2018 +0100 +++ b/client/src/lib/errors.js Tue Dec 04 09:55:31 2018 +0100 @@ -14,21 +14,26 @@ import app from "../main"; -const displayOptions = { - timeout: 2000, +let displayOptions = { + timeout: 2500, showProgressBar: false, - closeOnClick: false, + closeOnClick: true, pauseOnHover: true, oneAtTime: true, - bodyMaxLength: 250 + bodyMaxLength: 1024 }; -const displayError = ({ title, message }) => { +const displayError = ({ title, message, options }) => { + if (!options) options = {}; + const mergedOptions = { ...displayOptions, ...options }; + app.$snotify.info(message, title, mergedOptions); app.$snotify.error(message, title, displayOptions); }; -const displayInfo = ({ title, message }) => { - app.$snotify.info(message, title, displayOptions); +const displayInfo = ({ title, message, options }) => { + if (!options) options = {}; + const mergedOptions = { ...displayOptions, ...options }; + app.$snotify.info(message, title, mergedOptions); }; export { displayError, displayInfo }; diff -r d7152eb11d58 -r 5ad79db38ecf client/src/store/imports.js --- a/client/src/store/imports.js Mon Dec 03 20:46:35 2018 +0100 +++ b/client/src/store/imports.js Tue Dec 04 09:55:31 2018 +0100 @@ -17,9 +17,9 @@ /* eslint-disable no-unused-vars */ /* eslint-disable no-unreachable */ const STATES = { - NEEDSAPPROVAL: "NEEDSAPPROVAL", - APPROVED: "APPROVED", - REJECTED: "REJECTED" + NEEDSAPPROVAL: "pending", + APPROVED: "accepted", + REJECTED: "declined" }; const SCHEDULES = { diff -r d7152eb11d58 -r 5ad79db38ecf pkg/controllers/importqueue.go --- a/pkg/controllers/importqueue.go Mon Dec 03 20:46:35 2018 +0100 +++ b/pkg/controllers/importqueue.go Tue Dec 04 09:55:31 2018 +0100 @@ -334,6 +334,39 @@ INSERT INTO waterway.import_logs (import_id, msg) VALUES ($1, $2)` ) +func reviewImports( + reviews interface{}, + req *http.Request, + conn *sql.Conn, +) (JSONResult, error) { + + rs := *reviews.(*[]models.Review) + + type reviewResult struct { + ID int64 `json:"id"` + Message string `json:"message,omitempty"` + Error string `json:"error,omitempty"` + } + + results := make([]reviewResult, len(rs)) + + for i := range rs { + rev := &rs[i] + msg, err := decideImport(req, conn, rev.ID, string(rev.State)) + var errString string + if err != nil { + errString = err.Error() + } + results[i] = reviewResult{ + ID: rev.ID, + Message: msg, + Error: errString, + } + } + + return JSONResult{Result: results}, nil +} + func reviewImport( _ interface{}, req *http.Request, @@ -344,6 +377,27 @@ id, _ := strconv.ParseInt(vars["id"], 10, 64) state := vars["state"] + var msg string + if msg, err = decideImport(req, conn, id, state); err != nil { + return + } + + result := struct { + Message string `json:"message"` + }{ + Message: msg, + } + + jr = JSONResult{Result: &result} + return +} + +func decideImport( + req *http.Request, + conn *sql.Conn, + id int64, + state string, +) (message string, err error) { ctx := req.Context() var tx *sql.Tx if tx, err = conn.BeginTx(ctx, nil); err != nil { @@ -357,18 +411,12 @@ err = tx.QueryRowContext(ctx, isPendingSQL, id).Scan(&pending, &kind) switch { case err == sql.ErrNoRows: - err = JSONError{ - Code: http.StatusNotFound, - Message: fmt.Sprintf("Cannot find import #%d.", id), - } + err = fmt.Errorf("Cannot find import #%d.", id) return case err != nil: return case !pending: - err = JSONError{ - Code: http.StatusBadRequest, - Message: fmt.Sprintf("Import %d is not pending.", id), - } + err = fmt.Errorf("Import %d is not pending.", id) return } @@ -407,13 +455,8 @@ return } - result := struct { - Message string `json:"message"` - }{ - Message: fmt.Sprintf("Import #%d successfully changed to state '%s'.", - id, state), - } + message = fmt.Sprintf( + "Import #%d successfully changed to state '%s'.", id, state) - jr = JSONResult{Result: &result} return } diff -r d7152eb11d58 -r 5ad79db38ecf pkg/controllers/routes.go --- a/pkg/controllers/routes.go Mon Dec 03 20:46:35 2018 +0100 +++ b/pkg/controllers/routes.go Tue Dec 04 09:55:31 2018 +0100 @@ -182,6 +182,11 @@ Handle: importLogs, })).Methods(http.MethodGet) + api.Handle("/imports", waterwayAdmin(&JSONHandler{ + Input: func() interface{} { return &[]models.Review{} }, + Handle: reviewImports, + })).Methods(http.MethodPatch) + api.Handle("/imports/{id:[0-9]+}", waterwayAdmin(&JSONHandler{ Handle: deleteImport, })).Methods(http.MethodDelete) diff -r d7152eb11d58 -r 5ad79db38ecf pkg/models/import.go --- a/pkg/models/import.go Mon Dec 03 20:46:35 2018 +0100 +++ b/pkg/models/import.go Tue Dec 04 09:55:31 2018 +0100 @@ -15,6 +15,7 @@ import ( "encoding/json" + "errors" "time" ) @@ -36,8 +37,29 @@ Kind string `json:"kind"` Message string `json:"message"` } + + ReviewState string + + Review struct { + ID int64 `json:"id"` + State ReviewState `json:"state"` + } ) +var errInvalidReviewState = errors.New("state is wether 'accepted' nor 'declined'") + +func (rs *ReviewState) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + if s != "accepted" && s != "declined" { + return errInvalidReviewState + } + *rs = ReviewState(s) + return nil +} + func (it ImportTime) MarshalJSON() ([]byte, error) { return json.Marshal(it.Format("2006-01-02T15:04:05")) }