view pkg/imports/config.go @ 2006:35acb7f9ae0c

Do anything else before expectedly failing role creation Creating roles during database setup expectedly fails in case there already is another gemma database in the cluster. Doing it at the end of the transaction ensures it does not hide errors in other commands in the script. In passing, add the default admin via the designated view to ensure it will become a correctly set up application user.
author Tom Gottfried <tom@intevation.de>
date Thu, 24 Jan 2019 17:23:43 +0100
parents 59055c8301df
children 25967829cf00
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 imports

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"

	"github.com/robfig/cron"

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

type (
	// CronSpec is a string containing a cron line.
	CronSpec string

	// ImportKind is a string which has to be one
	// of the registered import types.
	ImportKind string

	// Config is JSON serialized form of a import configuration.
	Config struct {
		// Kind is the import type.
		Kind ImportKind `json:"kind"`
		// SendEMail indicates if a mail should be be send
		// when the import was changed to states
		// 'pending' or 'failed'.
		SendEMail bool `json:"send-email"`
		// Cron is the cron schedule
		// of this configuration if this value is not
		// nil. If nil the import is not scheduled.
		Cron *CronSpec `json:"cron"`
		// URL is an optional URL used by the import.
		URL *string `json:"url"`
		// Attributes are optional key/value pairs for a configuration.
		Attributes common.Attributes `json:"attributes,omitempty"`
	}

	// IDConfig is the same as Config with an ID.
	// Mainly used for server delivered configurations.
	IDConfig struct {
		ID         int64             `json:"id"`
		User       string            `json:"user"`
		Kind       ImportKind        `json:"kind"`
		SendEMail  bool              `json:"send-email"`
		Cron       *CronSpec         `json:"cron,omitempty"`
		URL        *string           `json:"url,omitempty"`
		Attributes common.Attributes `json:"attributes,omitempty"`
	}
)

// UnmarshalJSON checks if the incoming string
// is a registered import type.
func (ik *ImportKind) UnmarshalJSON(data []byte) error {
	var s string
	if err := json.Unmarshal(data, &s); err != nil {
		return err
	}

	if !HasImportKindName(s) {
		return fmt.Errorf("Unknown kind '%s'", s)
	}

	*ik = ImportKind(s)

	return nil
}

// UnmarshalJSON checks if the incoming string is
// a valid cron line.
func (cs *CronSpec) UnmarshalJSON(data []byte) error {
	var spec string
	if err := json.Unmarshal(data, &spec); err != nil {
		return err
	}
	if _, err := cron.Parse(spec); err != nil {
		return err
	}
	*cs = CronSpec(spec)

	return nil
}

const (
	configUser = "sys_admin"

	loadConfigSQL = `
SELECT
  username,
  kind,
  send_email,
  cron,
  url
FROM import.import_configuration
WHERE id = $1`

	loadConfigAttributesSQL = `
SELECT k, v
FROM import.import_configuration_attributes
WHERE import_configuration_id = $1`
)

// LoadIDConfigContext loads an import configuration from database.
func LoadIDConfigContext(ctx context.Context, conn *sql.Conn, id int64) (*IDConfig, error) {

	cfg := &IDConfig{ID: id}
	var kind ImportKind
	var cron, url sql.NullString
	err := conn.QueryRowContext(ctx, loadConfigSQL, id).Scan(
		&cfg.User,
		&kind,
		&cfg.SendEMail,
		&cron,
		&url,
	)

	switch {
	case err == sql.ErrNoRows:
		return nil, nil
	case err != nil:
		return nil, err
	}

	cfg.Kind = ImportKind(kind)
	if cron.Valid {
		c := CronSpec(cron.String)
		cfg.Cron = &c
	}
	if url.Valid {
		cfg.URL = &url.String
	}
	// load the extra attributes.
	rows, err := conn.QueryContext(ctx, loadConfigAttributesSQL, id)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var attributes common.Attributes
	for rows.Next() {
		var k, v string
		if err = rows.Scan(&k, &v); err != nil {
			return nil, err
		}
		if attributes == nil {
			attributes = common.Attributes{}
		}
		attributes[k] = v
	}
	if err = rows.Err(); err != nil {
		return nil, err
	}
	if len(attributes) > 0 {
		cfg.Attributes = attributes
	}
	return cfg, nil
}

func loadIDConfig(id int64) (*IDConfig, error) {
	return loadIDConfigContext(context.Background(), id)
}

func loadIDConfigContext(ctx context.Context, id int64) (*IDConfig, error) {
	var cfg *IDConfig
	err := auth.RunAs(ctx, configUser, func(conn *sql.Conn) error {
		var err error
		cfg, err = LoadIDConfigContext(ctx, conn, id)
		return err
	})
	return cfg, err
}