view pkg/controllers/importconfig.go @ 3762:98d5dd2f0ca1

Don't show unnecessary warnings when uploading TXT file for SR.
author Sascha Wilde <wilde@intevation.de>
date Wed, 26 Jun 2019 11:00:35 +0200
parents efa468653d48
children 8af156928a2d
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"
	"net/http"
	"strconv"

	"github.com/gorilla/mux"

	"gemma.intevation.de/gemma/pkg/auth"
	"gemma.intevation.de/gemma/pkg/common"
	"gemma.intevation.de/gemma/pkg/imports"
	"gemma.intevation.de/gemma/pkg/scheduler"
)

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

	id, _ := strconv.ParseInt(mux.Vars(req)["id"], 10, 64)

	ctx := req.Context()

	var jobID int64
	if jobID, err = imports.RunConfiguredImportContext(ctx, conn, id); err != nil {
		return
	}

	var result = struct {
		ID int64 `json:"id"`
	}{
		ID: jobID,
	}

	jr = JSONResult{
		Code:   http.StatusCreated,
		Result: &result,
	}
	return
}

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

	ctx := req.Context()

	raw := input.(*json.RawMessage)

	id, _ := strconv.ParseInt(mux.Vars(req)["id"], 10, 64)

	var pc *imports.PersistentConfig
	pc, err = imports.LoadPersistentConfigContext(ctx, conn, id)
	switch {
	case err == sql.ErrNoRows:
		err = JSONError{
			Code:    http.StatusNotFound,
			Message: fmt.Sprintf("No configuration %d found", id),
		}
		return
	case err != nil:
		return
	}

	kind := imports.JobKind(pc.Kind)
	ctor := imports.ImportModelForJobKind(kind)
	if ctor == nil {
		err = JSONError{
			Code:    http.StatusInternalServerError,
			Message: fmt.Sprintf("No constructor for kind '%s' found", pc.Kind),
		}
		return
	}
	config := ctor()
	if err = json.Unmarshal(*raw, config); err != nil {
		return
	}

	_, oldCron := pc.Attributes.Get("cron")

	session, _ := auth.GetSession(req)
	// When a password is stored it doesn't get retransmitted to the client
	// in order to prevent password leakage
	// When the user changes the import configuration without a new password
	// the old password should be conserved
	oldPasswd, ok := pc.Attributes["password"]
	pc.User = session.User
	pc.Attributes = common.Attributes{}
	if ok == true {
		pc.Attributes["password"] = oldPasswd
	}
	pc.Attributes.Marshal(config) // Marshal only overwrites keys present in config
	cron, newCron := pc.Attributes.Get("cron")

	var tx *sql.Tx
	if tx, err = conn.BeginTx(ctx, nil); err != nil {
		return
	}
	defer tx.Rollback()

	if err = pc.UpdateContext(ctx, tx); err != nil {
		return
	}

	if oldCron {
		scheduler.UnbindByID(id)
	}

	if newCron {
		if err = scheduler.BindAction(
			string(pc.Kind),
			cron,
			id,
		); err != nil {
			return
		}
	}

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

	var result = struct {
		ID int64 `json:"id"`
	}{
		ID: id,
	}

	jr = JSONResult{Result: &result}
	return
}

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

	ctx := req.Context()

	id, _ := strconv.ParseInt(mux.Vars(req)["id"], 10, 64)

	var cfg *imports.PersistentConfig

	cfg, err = imports.LoadPersistentConfigContext(ctx, conn, id)
	switch {
	case err != nil:
		return
	case cfg == nil:
		err = JSONError{
			Code:    http.StatusNotFound,
			Message: fmt.Sprintf("No schedule %d found", id),
		}
		return
	}

	kind := imports.JobKind(cfg.Kind)

	ctor := imports.ImportModelForJobKind(kind)
	if ctor == nil {
		err = JSONError{
			Code:    http.StatusInternalServerError,
			Message: fmt.Sprintf("No constructor for kind '%s' found", cfg.Kind),
		}
		return
	}

	// Remove `password` from the attributes to be delivered to the client.
	// Even a priviledged user shall not be able to see the password.
	// (See config.ListAllPersistentConfigurationsContext() for the other
	//  place where this is done.)
	filteredAttributes := make(common.Attributes)
	for key, value := range cfg.Attributes {
		if key != "password" {
			filteredAttributes[key] = value
		}
	}

	what := ctor()
	if err = filteredAttributes.Unmarshal(what); err != nil {
		return
	}

	jr = JSONResult{Result: &imports.ImportConfigOut{
		ID:     id,
		Kind:   imports.ImportKind(cfg.Kind),
		Config: what,
	}}
	return
}

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

	ctx := req.Context()

	id, _ := strconv.ParseInt(mux.Vars(req)["id"], 10, 64)

	var tx *sql.Tx
	if tx, err = conn.BeginTx(ctx, nil); err != nil {
		return
	}
	defer tx.Rollback()

	err = imports.DeletePersistentConfigurationContext(
		ctx,
		tx,
		id,
	)

	switch {
	case err == sql.ErrNoRows:
		err = JSONError{
			Code:    http.StatusNotFound,
			Message: fmt.Sprintf("No configuration %d found", id),
		}
		return
	case err != nil:
		return
	}

	// Remove from running scheduler.
	scheduler.UnbindByID(id)

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

	var result = struct {
		ID int64 `json:"id"`
	}{
		ID: id,
	}

	jr = JSONResult{Result: &result}

	return
}

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

	cfg := input.(*imports.ImportConfigIn)

	kind := imports.JobKind(cfg.Kind)

	ctor := imports.ImportModelForJobKind(kind)
	if ctor == nil {
		err = JSONError{
			Code:    http.StatusBadRequest,
			Message: fmt.Sprintf("No kind %s found", string(cfg.Kind)),
		}
		return
	}
	config := ctor()
	if err = json.Unmarshal(cfg.Config, config); err != nil {
		return
	}

	session, _ := auth.GetSession(req)

	pc := imports.PersistentConfig{
		User:       session.User,
		Kind:       string(cfg.Kind),
		Attributes: common.Attributes{},
	}
	pc.Attributes.Marshal(config)

	ctx := req.Context()

	var tx *sql.Tx
	if tx, err = conn.BeginTx(ctx, nil); err != nil {
		return
	}
	defer tx.Rollback()

	var id int64
	if id, err = pc.StoreContext(ctx, tx); err != nil {
		return
	}

	// Need to start a scheduler job right away?
	if cron, ok := pc.Attributes.Get("cron"); ok {
		if err = scheduler.BindAction(string(cfg.Kind), cron, id); err != nil {
			return
		}
	}

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

	var result = struct {
		ID int64 `json:"id"`
	}{
		ID: id,
	}

	jr = JSONResult{
		Code:   http.StatusCreated,
		Result: &result,
	}
	return
}

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

	ctx := req.Context()
	configs := []*imports.ImportConfigOut{}

	if err = imports.ListAllPersistentConfigurationsContext(
		ctx, conn,
		func(config *imports.ImportConfigOut) error {
			configs = append(configs, config)
			return nil
		},
	); err != nil {
		return
	}
	jr = JSONResult{Result: configs}
	return
}