view pkg/controllers/printtemplates.go @ 3217:4c254651d80b

Added template types "map", "diagram", "report".
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Thu, 09 May 2019 12:08:02 +0200
parents 813309225e35
children 9e087a495f41
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"
)

const maxPrintTemplateSize = 5 * 1024 * 1024

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

	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 users.current_user_country()
  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(
	_ interface{},
	req *http.Request,
	conn *sql.Conn,
) (jr JSONResult, err error) {

	ts := mux.Vars(req)["types"]

	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 = conn.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, &w, &country); err != nil {
			return
		}
		tmpl.Time = models.Time{Time: w}
		if country.Valid {
			tmpl.Country = &country.String
		}
		templates = append(templates, &tmpl)
	}

	jr = JSONResult{Result: templates}
	return
}

func fetchPrintTemplate(
	_ interface{},
	req *http.Request,
	conn *sql.Conn,
) (jr JSONResult, err error) {

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

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

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

func createPrintTemplate(
	input interface{},
	req *http.Request,
	conn *sql.Conn,
) (jr JSONResult, err error) {

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

	in := input.(*json.RawMessage)

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

	ctx := req.Context()
	var tx *sql.Tx
	if tx, err = conn.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 = 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 = JSONResult{
		Code: http.StatusCreated,
		Result: map[string]string{
			"created": name,
		},
	}
	return
}

func deletePrintTemplate(
	_ interface{},
	req *http.Request,
	conn *sql.Conn,
) (jr JSONResult, err error) {

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

	ctx := req.Context()
	var tx *sql.Tx
	if tx, err = conn.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 = JSONError{
			Code:    http.StatusNotFound,
			Message: "No such template found",
		}
		return
	case err != nil:
		return
	case !dummy:
		err = 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 = JSONResult{
		Result: map[string]string{
			"deleted": name,
		},
	}

	return
}

func updatePrintTemplate(
	input interface{},
	req *http.Request,
	conn *sql.Conn,
) (jr JSONResult, err error) {

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

	in := input.(*json.RawMessage)

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

	ctx := req.Context()
	var tx *sql.Tx
	if tx, err = conn.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 = JSONError{
			Code:    http.StatusNotFound,
			Message: "No such template found",
		}
		return
	case err != nil:
		return
	case !dummy:
		err = 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 = JSONResult{
		Code: http.StatusOK,
		Result: map[string]string{
			"updated": name,
		},
	}
	return
}