view pkg/controllers/json.go @ 1234:1a5564655f2a

refac: Sidebar reorganized In order to make context switches between administrative tasks which are map related and those which are system related, we now have a category "administration" and "systemadministration". The Riverbedmorphology does nothing than display the map, so it is renamed to that (map). In case the context of "systemadministration" is chosen, the "map" brings you just back to the map.
author Thomas Junk <thomas.junk@intevation.de>
date Tue, 20 Nov 2018 09:54:53 +0100
parents a244b18cb916
children cabf4789e02b
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 (
	"database/sql"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"

	"github.com/jackc/pgx"

	"gemma.intevation.de/gemma/pkg/auth"
)

type JSONResult struct {
	Code   int
	Result interface{}
}

type JSONHandler struct {
	Input  func() interface{}
	Handle func(interface{}, *http.Request, *sql.Conn) (JSONResult, error)
	NoConn bool
}

type JSONError struct {
	Code    int
	Message string
}

func (je JSONError) Error() string {
	return fmt.Sprintf("%d: %s", je.Code, je.Message)
}

func (j *JSONHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {

	var input interface{}
	if j.Input != nil {
		input = j.Input()
		defer req.Body.Close()
		if err := json.NewDecoder(req.Body).Decode(input); err != nil {
			http.Error(rw, "error: "+err.Error(), http.StatusBadRequest)
			return
		}
	}

	var jr JSONResult
	var err error

	if token, ok := auth.GetToken(req); ok && !j.NoConn {
		if session := auth.Sessions.Session(token); session != nil {
			err = auth.RunAs(session.User, req.Context(), func(conn *sql.Conn) error {
				jr, err = j.Handle(input, req, conn)
				return err
			})
		} else {
			err = auth.ErrNoSuchToken
		}
	} else {
		jr, err = j.Handle(input, req, nil)
	}

	if err != nil {
		log.Printf("error: %v\n", err)
		switch e := err.(type) {
		case pgx.PgError:
			var res = struct {
				Result  string `json:"result"`
				Code    string `json:"code"`
				Message string `json:"message"`
			}{
				Result:  "failure",
				Code:    e.Code,
				Message: e.Message,
			}
			rw.Header().Set("Content-Type", "application/json")
			rw.WriteHeader(http.StatusInternalServerError)
			if err := json.NewEncoder(rw).Encode(&res); err != nil {
				log.Printf("error: %v\n", err)
			}
		case JSONError:
			rw.Header().Set("Content-Type", "application/json")
			if e.Code == 0 {
				e.Code = http.StatusInternalServerError
			}
			rw.WriteHeader(e.Code)
			var res = struct {
				Message string `json:"message"`
			}{
				Message: e.Message,
			}
			if err := json.NewEncoder(rw).Encode(&res); err != nil {
				log.Printf("error: %v\n", err)
			}
		default:
			http.Error(rw,
				"error: "+err.Error(),
				http.StatusInternalServerError)
		}
		return
	}

	if jr.Code == 0 {
		jr.Code = http.StatusOK
	}

	if jr.Code != http.StatusNoContent {
		rw.Header().Set("Content-Type", "application/json")
	}
	rw.WriteHeader(jr.Code)
	if jr.Code != http.StatusNoContent {
		var err error
		if r, ok := jr.Result.(io.Reader); ok {
			_, err = io.Copy(rw, r)
		} else {
			err = json.NewEncoder(rw).Encode(jr.Result)
		}
		if err != nil {
			log.Printf("error: %v\n", err)
		}
	}
}

func SendJSON(rw http.ResponseWriter, code int, data interface{}) {
	rw.Header().Set("Content-Type", "application/json")
	rw.WriteHeader(code)
	if err := json.NewEncoder(rw).Encode(data); err != nil {
		log.Printf("error: %v\n", err)
	}
}