view pkg/models/intservices.go @ 876:8b9bd9ccdd93 geo-style

Upload style during boot. TODO: Connect with layer.
author Sascha L. Teichmann <teichmann@intevation.de>
date Sun, 30 Sep 2018 19:42:16 +0200
parents ad9272460ef3
children 254cd247826d
line wrap: on
line source

package models

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"net/http"
	"strings"
	"sync"

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

type IntEntry struct {
	Name  string         `json:"name"`
	Style sql.NullString `json:"-"` // This should be done separately.
	WMS   bool           `json:"wms"`
	WFS   bool           `json:"wfs"`
}

type IntServices struct {
	entries []IntEntry
	mu      sync.Mutex
}

const (
	selectPublishedServices = `SELECT relname, style, as_wms, as_wfs
FROM sys_admin.published_services JOIN pg_class ON name = oid ORDER by relname`

	updateStyleSQL = `
UPDATE sys_admin.published_services
SET style = xslt_process($1, $2, $3)::bytea
WHERE name IN (SELECT oid FROM pg_class WHERE relname = $4)`
)

const (
	replaceNameXSLT = `<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:sld="http://www.opengis.net/sld"
  xmlns:se="http://www.opengis.net/se">

  <xsl:param name="name"/>

  <xsl:template
    match="/sld:StyledLayerDescriptor/sld:NamedLayer/sld:Name/text()">
	<xsl:value-of select="$name"/>
  </xsl:template>

  <xsl:template
    match="/sld:StyledLayerDescriptor/sld:NamedLayer/se:Name/text()">
	<xsl:value-of select="$name"/>
  </xsl:template>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>`
)

var InternalServices = &IntServices{}

func UpdateInternalStyle(req *http.Request, name, style string) error {
	return auth.RunAsSessionUser(req, func(conn *sql.Conn) error {
		param := fmt.Sprintf("name='%s'", strings.Replace(name, ",", "", -1))
		_, err := conn.ExecContext(
			req.Context(), updateStyleSQL,
			style, replaceNameXSLT, param, name)
		if err == nil {
			InternalServices.Invalidate()
		}
		return err
	})
}

func (ps *IntServices) Find(name string) (string, bool) {
	ps.mu.Lock()
	defer ps.mu.Unlock()

	if ps.entries == nil {
		if err := ps.load(); err != nil {
			log.Printf("error: %v\n", err)
			return "", false
		}
	}

	if ps.has(name) {
		return config.GeoServerURL() + "/" + name, true
	}
	return "", false
}

func (ps *IntServices) has(service string) bool {
	var check func(*IntEntry) bool
	switch service {
	case "wms":
		check = func(e *IntEntry) bool { return e.WMS }
	case "wfs":
		check = func(e *IntEntry) bool { return e.WFS }
	default:
		return false
	}
	for i := range ps.entries {
		if check(&ps.entries[i]) {
			return true
		}
	}
	return false
}

func (ps *IntServices) load() error {
	// make empty slice to prevent retry if slice is empty.
	ps.entries = []IntEntry{}
	return auth.RunAs("sys_admin", context.Background(),
		func(conn *sql.Conn) error {
			rows, err := conn.QueryContext(
				context.Background(), selectPublishedServices)
			if err != nil {
				return err
			}
			defer rows.Close()
			for rows.Next() {
				var entry IntEntry
				if err := rows.Scan(
					&entry.Name, &entry.Style,
					&entry.WMS, &entry.WFS,
				); err != nil {
					return err
				}
				ps.entries = append(ps.entries, entry)
			}
			return rows.Err()
		})
}

func (ps *IntServices) Invalidate() {
	ps.mu.Lock()
	ps.entries = nil
	ps.mu.Unlock()
}

func InternalAll(IntEntry) bool  { return true }
func IntWMS(entry IntEntry) bool { return entry.WMS }
func IntWFS(entry IntEntry) bool { return entry.WFS }

func IntByName(name string) func(IntEntry) bool {
	return func(entry IntEntry) bool { return entry.Name == name }
}

func IntAnd(a, b func(IntEntry) bool) func(IntEntry) bool {
	return func(entry IntEntry) bool { return a(entry) && b(entry) }
}

func IntWithStyle(entry IntEntry) bool {
	return entry.Style.Valid
}

func (ps *IntServices) Filter(accept func(IntEntry) bool) []IntEntry {
	ps.mu.Lock()
	defer ps.mu.Unlock()
	if ps.entries == nil {
		if err := ps.load(); err != nil {
			log.Printf("error: %v\n", err)
			return nil
		}
	}
	pe := make([]IntEntry, 0, len(ps.entries))
	for _, e := range ps.entries {
		if accept(e) {
			pe = append(pe, e)
		}
	}

	return pe
}