view pkg/controllers/printtemplates.go @ 5564:aaa9e658cabd

Log export: Added marker interface to JobCreators that log messages should be loaded at export. Removes the hard coded SR export SQL logic.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Sat, 12 Feb 2022 18:56:55 +0100
parents 4394daeea96a
children 6270951dda28
line wrap: on
line source

// This is Free Software under GNU Affero General Public License v >= 3.0
// without warranty, see README.md and license for details.
//
// SPDX-License-Identifier: AGPL-3.0-or-later
// License-Filename: LICENSES/AGPL-3.0.txt
//
// Copyright (C) 2018 by via donau
//   – Österreichische Wasserstraßen-Gesellschaft mbH
// Software engineering by Intevation GmbH
//
// Author(s):
//  * Sascha L. Teichmann <sascha.teichmann@intevation.de>

package controllers

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"net/http"
	"strings"
	"time"

	"github.com/gorilla/mux"
	"github.com/jackc/pgx/pgtype"

	"gemma.intevation.de/gemma/pkg/models"

	mw "gemma.intevation.de/gemma/pkg/middleware"
)

const maxPrintTemplateSize = 5 * 1024 * 1024

const (
	listPrintTemplatesSQL = `
SELECT
  template_name,
  template_type::varchar,
  date_info,
  country
FROM users.templates
WHERE
`

	hasPrintTemplateSQL = `
SELECT true FROM users.templates
WHERE template_name = $1 AND template_type = $2::template_types`

	deletePrintTemplateSQL = `
DELETE FROM users.templates
WHERE template_name = $1 AND template_type = $2::template_types`

	selectPrintTemplateSQL = `
SELECT template_data FROM users.templates
WHERE template_name = $1 AND template_type = $2::template_types`

	insertPrintTemplateSQL = `
INSERT INTO users.templates (template_name, template_type, template_data, country)
SELECT
  $1,
  $2::template_types,
  $3,
  CASE WHEN pg_has_role('sys_admin', 'MEMBER') THEN NULL
       ELSE (SELECT country FROM users.list_users
             WHERE username = current_user)
  END`

	updatePrintTemplateSQL = `
UPDATE user.templates template_data = $2
WHERE template_name = $1 AND template_type = $2::template_types`
)

var templateTypes = []string{"map", "diagram", "report"}

func listPrintTemplates(req *http.Request) (jr mw.JSONResult, err error) {

	ts := mux.Vars(req)["type"]
	if ts == "" {
		if ts = req.FormValue("types"); ts == "" {
			ts = strings.Join(templateTypes, ",")
		}
	}

	types := toTextArray(ts, templateTypes)
	filter := buildFilterTerm("template_type = ANY($%d) ", types)

	var stmt strings.Builder
	var args []interface{}

	stmt.WriteString(listPrintTemplatesSQL)
	filter.serialize(&stmt, &args)
	stmt.WriteString(" ORDER BY date_info DESC")

	var rows *sql.Rows
	if rows, err = mw.JSONConn(req).QueryContext(req.Context(), stmt.String(), args...); err != nil {
		return
	}
	defer rows.Close()

	type template struct {
		Name    string      `json:"name"`
		Type    string      `json:"type"`
		Time    models.Time `json:"time"`
		Country *string     `json:"country,omitempty"`
	}

	templates := []*template{}

	for rows.Next() {
		var tmpl template
		var w time.Time
		var country sql.NullString
		if err = rows.Scan(
			&tmpl.Name,
			&tmpl.Type,
			&w,
			&country,
		); err != nil {
			return
		}
		tmpl.Time = models.Time{Time: w}
		if country.Valid {
			tmpl.Country = &country.String
		}
		templates = append(templates, &tmpl)
	}

	jr = mw.JSONResult{Result: templates}
	return
}

func fetchPrintTemplate(req *http.Request) (jr mw.JSONResult, err error) {

	vars := mux.Vars(req)
	name, typ := vars["name"], vars["type"]

	ctx := req.Context()
	var data pgtype.Bytea
	err = mw.JSONConn(req).QueryRowContext(ctx, selectPrintTemplateSQL, name, typ).Scan(&data)

	switch {
	case err == sql.ErrNoRows:
		err = mw.JSONError{
			Code:    http.StatusNotFound,
			Message: "No such template found",
		}
		return
	case err != nil:
		return
	case data.Status != pgtype.Present:
		err = mw.JSONError{
			Code:    http.StatusInternalServerError,
			Message: "Unexpected return value from database query",
		}
		return
	}
	jr = mw.JSONResult{Result: bytes.NewReader(data.Bytes)}
	return
}

func createPrintTemplate(req *http.Request) (jr mw.JSONResult, err error) {

	vars := mux.Vars(req)
	name, typ := vars["name"], vars["type"]

	in := mw.JSONInput(req).(*json.RawMessage)

	if name == "" {
		err = mw.JSONError{
			Code:    http.StatusBadRequest,
			Message: "Template must have a none empty name",
		}
		return
	}
	if len(*in) == 0 {
		err = mw.JSONError{
			Code:    http.StatusBadRequest,
			Message: "Template must have a none empty template",
		}
		return
	}

	ctx := req.Context()
	var tx *sql.Tx
	if tx, err = mw.JSONConn(req).BeginTx(ctx, nil); err != nil {
		return
	}
	defer tx.Rollback()

	var dummy bool
	err = tx.QueryRowContext(ctx, hasPrintTemplateSQL, name, typ).Scan(&dummy)

	switch {
	case err == sql.ErrNoRows:
		// This is fine.
	case err != nil:
		return
	default:
		err = mw.JSONError{
			Code:    http.StatusBadRequest,
			Message: "A template with this name already exists",
		}
		return
	}
	data := pgtype.Bytea{Bytes: *in, Status: pgtype.Present}

	if _, err = tx.ExecContext(ctx, insertPrintTemplateSQL, name, typ, &data); err != nil {
		return
	}

	if err = tx.Commit(); err != nil {
		return
	}
	jr = mw.JSONResult{
		Code: http.StatusCreated,
		Result: map[string]string{
			"created": name,
		},
	}
	return
}

func deletePrintTemplate(req *http.Request) (jr mw.JSONResult, err error) {

	vars := mux.Vars(req)
	name, typ := vars["name"], vars["type"]

	ctx := req.Context()
	var tx *sql.Tx
	if tx, err = mw.JSONConn(req).BeginTx(ctx, nil); err != nil {
		return
	}
	defer tx.Rollback()

	var dummy bool
	err = tx.QueryRowContext(ctx, hasPrintTemplateSQL, name, typ).Scan(&dummy)

	switch {
	case err == sql.ErrNoRows:
		err = mw.JSONError{
			Code:    http.StatusNotFound,
			Message: "No such template found",
		}
		return
	case err != nil:
		return
	case !dummy:
		err = mw.JSONError{
			Code:    http.StatusInternalServerError,
			Message: "Unexpected return value from database query",
		}
		return
	}

	if _, err = tx.ExecContext(ctx, deletePrintTemplateSQL, name, typ); err != nil {
		return
	}

	if err = tx.Commit(); err != nil {
		return
	}

	jr = mw.JSONResult{
		Result: map[string]string{
			"deleted": name,
		},
	}

	return
}

func updatePrintTemplate(req *http.Request) (jr mw.JSONResult, err error) {

	vars := mux.Vars(req)
	name, typ := vars["name"], vars["type"]

	in := mw.JSONInput(req).(*json.RawMessage)

	if name == "" {
		err = mw.JSONError{
			Code:    http.StatusBadRequest,
			Message: "Template must have a none empty name",
		}
		return
	}
	if len(*in) == 0 {
		err = mw.JSONError{
			Code:    http.StatusBadRequest,
			Message: "Template must have a none empty template",
		}
		return
	}

	ctx := req.Context()
	var tx *sql.Tx
	if tx, err = mw.JSONConn(req).BeginTx(ctx, nil); err != nil {
		return
	}
	defer tx.Rollback()

	var dummy bool
	err = tx.QueryRowContext(ctx, hasPrintTemplateSQL, name, typ).Scan(&dummy)

	switch {
	case err == sql.ErrNoRows:
		err = mw.JSONError{
			Code:    http.StatusNotFound,
			Message: "No such template found",
		}
		return
	case err != nil:
		return
	case !dummy:
		err = mw.JSONError{
			Code:    http.StatusInternalServerError,
			Message: "Unexpected return value from database query",
		}
		return
	}
	data := pgtype.Bytea{Bytes: *in, Status: pgtype.Present}

	if _, err = tx.ExecContext(ctx, updatePrintTemplateSQL, name, typ, &data); err != nil {
		return
	}

	if err = tx.Commit(); err != nil {
		return
	}

	jr = mw.JSONResult{
		Code: http.StatusOK,
		Result: map[string]string{
			"updated": name,
		},
	}
	return
}