view pkg/imports/config.go @ 2049:d1a680be7ae4 unify_imports

Imports. Re-enabled /imports/config GET.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Sun, 27 Jan 2019 14:27:37 +0100
parents 10a1e139d2e8
children a3ccef8ec304
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"

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

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

	ImportConfigIn struct {
		Kind   ImportKind      `json:"kind"`
		Config json.RawMessage `json:"config"`
	}

	ImportConfigOut struct {
		ID     int64       `json:"id"`
		Kind   ImportKind  `json:"kind"`
		User   string      `json:"user"`
		Config interface{} `json:"config,omitempty"`
	}

	PersistentConfig struct {
		ID         int64
		User       string
		Kind       string
		Attributes common.Attributes
	}
)

// 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
}

const (
	configUser = "sys_admin"

	loadPersistentConfigSQL = `
SELECT
  username,
  kind
FROM import.import_configuration
WHERE id = $1`

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

	deleteImportConfiguationAttributesSQL = `
DELETE FROM import.import_configuration_attributes
WHERE import_configuration_id = $1`

	updateImportConfigurationSQL = `
UPDATE import.import_configuration SET
  username = $2,
  kind = $3
WHERE id = $1`

	selectImportConfigurationsByID = `
SELECT
  c.id AS id,
  username,
  kind,
  a.k,
  a.v
FROM import.import_configuration c JOIN 
  import.import_configuration_attributes a
  ON c.id = a.import_configuration_id
ORDER by c.id`
)

func (pc *PersistentConfig) Update(ctx context.Context, tx *sql.Tx) error {
	if _, err := tx.ExecContext(ctx, deleteImportConfiguationAttributesSQL, pc.ID); err != nil {
		return err
	}
	return nil
}

func LoadPersistentConfigContext(ctx context.Context, conn *sql.Conn, id int64) (*PersistentConfig, error) {

	cfg := &PersistentConfig{ID: id}

	err := conn.QueryRowContext(ctx, loadPersistentConfigSQL, id).Scan(
		&cfg.User,
		&cfg.Kind,
	)

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

	// load the extra attributes.
	rows, err := conn.QueryContext(ctx, loadPersistentConfigAttributesSQL, 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 loadPersistentConfig(id int64) (*PersistentConfig, error) {
	return loadPersistentConfigContext(context.Background(), id)
}

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

func ListAllPersistentConfigurationsContext(
	ctx context.Context,
	conn *sql.Conn,
	fn func(*ImportConfigOut) error,
) error {

	rows, err := conn.QueryContext(ctx, selectImportConfigurationsByID)
	if err != nil {
		return err
	}
	defer rows.Close()

	var (
		first  = true
		lastID int64
		pc     PersistentConfig
		k, v   sql.NullString
	)

	send := func() error {
		kind := JobKind(pc.Kind)
		ctor := ImportModelForJobKind(kind)
		if ctor == nil {
			return fmt.Errorf("unable to deserialize kind '%s'", pc.Kind)
		}
		config := ctor()
		pc.Attributes.Unmarshal(config)
		return fn(&ImportConfigOut{
			ID:     pc.ID,
			Kind:   ImportKind(pc.Kind),
			User:   pc.User,
			Config: config,
		})
	}

	for rows.Next() {
		if err := rows.Scan(&pc.ID, &pc.User, &k, &v); err != nil {
			return err
		}
		if !first {
			if lastID != pc.ID {
				if err := send(); err != nil {
					return err
				}
				pc.Attributes = nil
			}
		} else {
			first = false
		}

		if k.Valid && v.Valid {
			if pc.Attributes == nil {
				pc.Attributes = common.Attributes{}
			}
			pc.Attributes.Set(k.String, v.String)
		}

		lastID = pc.ID
	}

	if err := rows.Err(); err != nil {
		return err
	}

	err = nil
	if !first {
		err = send()
	}
	return err
}