view pkg/controllers/routes.go @ 3681:c9e1848a516a

Handle violation of responsibility areas graceful for BN import. As many others this is an error, which should only affect the specific bottleneck failing and not the whole import.
author Sascha Wilde <wilde@intevation.de>
date Tue, 18 Jun 2019 12:43:01 +0200
parents 6693be57b7a2
children ed4820efb7e6
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>
//  * Sascha Wilde <sascha.wilde@intevation.de>

package controllers

import (
	"encoding/json"
	"net/http"
	"net/http/httputil"
	"strings"

	"github.com/gorilla/mux"

	"gemma.intevation.de/gemma/pkg/auth"
	"gemma.intevation.de/gemma/pkg/imports"
	"gemma.intevation.de/gemma/pkg/middleware"
	"gemma.intevation.de/gemma/pkg/models"
)

// BindRoutes binds all the API endpoints to the exposed router.
func BindRoutes(m *mux.Router) {

	api := m.PathPrefix("/api").Subrouter()

	var (
		sysAdmin      = auth.EnsureRole("sys_admin")
		waterwayAdmin = auth.EnsureRole("waterway_admin")
		any           = auth.EnsureRole("sys_admin", "waterway_admin", "waterway_user")
	)

	// User management.
	api.Handle("/users", any(&JSONHandler{
		Handle: listUsers,
	})).Methods(http.MethodGet)

	api.Handle("/users", sysAdmin(&JSONHandler{
		Input:  func(*http.Request) interface{} { return new(models.User) },
		Handle: createUser,
	})).Methods(http.MethodPost)

	api.Handle("/users/{user}", any(&JSONHandler{
		Handle: listUser,
	})).Methods(http.MethodGet)

	api.Handle("/users/{user}", any(&JSONHandler{
		Input:  func(*http.Request) interface{} { return new(models.User) },
		Handle: updateUser,
	})).Methods(http.MethodPut)

	api.Handle("/users/{user}", sysAdmin(&JSONHandler{
		Handle: deleteUser,
	})).Methods(http.MethodDelete)

	// System notifications
	api.Handle("/testmail/{user}", sysAdmin(&JSONHandler{
		Handle: sendTestMail,
	})).Methods(http.MethodGet)

	// System Management
	api.Handle("/system/log/{service}/{file}", sysAdmin(&JSONHandler{
		Handle: showSystemLog,
		NoConn: true,
	})).Methods(http.MethodGet)

	// System Settings
	api.Handle("/system/config", any(&JSONHandler{
		Handle: getSystemConfig,
		NoConn: true,
	})).Methods(http.MethodGet)

	api.Handle("/system/settings", any(&JSONHandler{
		Handle: getSystemSettings,
	})).Methods(http.MethodGet)

	api.Handle("/system/settings", any(&JSONHandler{
		Input:  func(*http.Request) interface{} { return &map[string]string{} },
		Handle: setSystemSettings,
	})).Methods(http.MethodPut)

	api.Handle("/system/style/{feature}/{attr}", any(&JSONHandler{
		Handle: getFeatureStyle,
	})).Methods(http.MethodGet)

	api.Handle("/system/style/{feature}/{attr}", any(&JSONHandler{
		Input:  func(*http.Request) interface{} { return new(models.Colour) },
		Handle: setFeatureStyle,
	})).Methods(http.MethodPut)

	// Password resets.
	api.Handle("/users/passwordreset", &JSONHandler{
		Input:  func(*http.Request) interface{} { return new(models.PWResetUser) },
		Handle: passwordResetRequest,
		NoConn: true,
	}).Methods(http.MethodPost)

	api.Handle("/users/passwordreset/{hash}", &JSONHandler{
		Handle: passwordReset,
		NoConn: true,
	}).Methods(http.MethodGet)

	// Print templates
	api.Handle("/templates", any(&JSONHandler{
		Handle: listPrintTemplates,
	})).Methods(http.MethodGet)

	tTypes := "{type:" + strings.Join(templateTypes, "|") + "}"

	api.Handle("/templates/"+tTypes, any(&JSONHandler{
		Handle: listPrintTemplates,
	})).Methods(http.MethodGet)

	api.Handle("/templates/"+tTypes+"/{name}", any(&JSONHandler{
		Handle: fetchPrintTemplate,
	})).Methods(http.MethodGet)

	api.Handle("/templates/"+tTypes+"/{name}", waterwayAdmin(&JSONHandler{
		Input:  func(*http.Request) interface{} { return &json.RawMessage{} },
		Handle: createPrintTemplate,
		Limit:  maxPrintTemplateSize,
	})).Methods(http.MethodPost)

	api.Handle("/templates/"+tTypes+"/{name}", waterwayAdmin(&JSONHandler{
		Handle: deletePrintTemplate,
	})).Methods(http.MethodDelete)

	api.Handle("/templates/"+tTypes+"/{name}", waterwayAdmin(&JSONHandler{
		Input:  func(*http.Request) interface{} { return &json.RawMessage{} },
		Handle: updatePrintTemplate,
		Limit:  maxPrintTemplateSize,
	})).Methods(http.MethodPatch)

	// External proxies.
	external := middleware.NotFound(&httputil.ReverseProxy{
		Director:       proxyDirector(models.ExternalServices.Find),
		ModifyResponse: proxyModifyResponse("/api/external/"),
	})

	externalAuth := any(external)

	api.Handle("/external/{hash}/{url}", externalAuth).
		Methods(
			http.MethodGet, http.MethodPost,
			http.MethodPut, http.MethodDelete)

	api.Handle("/external/{entry}", externalAuth).
		Methods(
			http.MethodGet, http.MethodPost,
			http.MethodPut, http.MethodDelete)

	// Internal proxies.
	internal := middleware.NotFound(&httputil.ReverseProxy{
		Director:       proxyDirector(models.InternalServices.Find),
		ModifyResponse: proxyModifyResponse("/api/internal/"),
	})

	internalAuth := any(
		middleware.ModifyQuery(internal, middleware.InjectUser))

	api.Handle("/internal/{hash}/{url}", internalAuth).
		Methods(
			http.MethodGet, http.MethodPost,
			http.MethodPut, http.MethodDelete)

	api.Handle("/internal/{entry}", internalAuth).
		Methods(
			http.MethodGet, http.MethodPost,
			http.MethodPut, http.MethodDelete)

	api.Handle("/published", any(&JSONHandler{
		Handle: published,
		NoConn: true,
	})).Methods(http.MethodGet)

	// Survey selection
	api.Handle("/surveys/{bottleneck}", any(&JSONHandler{
		Handle: listSurveys,
	})).Methods(http.MethodGet)

	// Bottlenecks
	api.Handle("/bottlenecks", any(&JSONHandler{
		Handle: listBottlenecks,
	})).Methods(http.MethodGet)

	// difference calculation
	api.Handle("/diff", any(&JSONHandler{
		Input:  func(*http.Request) interface{} { return new(models.DiffCalculationInput) },
		Handle: diffCalculation,
	})).Methods(http.MethodPost)

	// Cross sections
	api.Handle("/cross", any(&JSONHandler{
		Input:  func(*http.Request) interface{} { return new(models.CrossSectionInput) },
		Handle: crossSection,
	})).Methods(http.MethodPost)

	// Feature search
	api.Handle("/search", any(&JSONHandler{
		Input:  func(*http.Request) interface{} { return new(models.SearchRequest) },
		Handle: searchFeature,
	})).Methods(http.MethodPost)

	// Geo styling
	api.Handle("/geo/style/{feature}",
		sysAdmin(http.HandlerFunc(uploadStyle))).Methods(http.MethodPost)

	// Imports
	api.Handle("/imports/sr-upload/{token}",
		waterwayAdmin(http.HandlerFunc(deleteSoundingUpload))).Methods(http.MethodDelete)

	api.Handle("/imports/sr-upload", waterwayAdmin(&JSONHandler{
		Handle: uploadSoundingResult,
	})).Methods(http.MethodPost)

	api.Handle("/imports/sr", waterwayAdmin(
		http.HandlerFunc(importSoundingResult))).Methods(http.MethodPost)

	api.Handle("/imports/wp", waterwayAdmin(
		importWaterwayProfiles())).Methods(http.MethodPost)

	api.Handle("/imports/agm", waterwayAdmin(
		importApprovedGaugeMeasurements())).Methods(http.MethodPost)

	api.Handle("/imports/ubn", waterwayAdmin(
		importUploadedBottleneck())).Methods(http.MethodPost)

	api.Handle("/imports/ufa", waterwayAdmin(
		importUploadedFairwayAvailability())).Methods(http.MethodPost)

	api.Handle("/imports/ugm", waterwayAdmin(
		importUploadedGaugeMeasurement())).Methods(http.MethodPost)

	api.Handle("/imports/{kind:st}", sysAdmin(&JSONHandler{
		Input:  importModel,
		Handle: manualImport,
		NoConn: true,
	})).Methods(http.MethodPost)

	kinds := strings.Join([]string{
		"bn", "gm", "fa", "wx", "wa",
		"wg", "dmv", "fd", "dma",
		"sec",
	}, "|")

	api.Handle("/imports/{kind:"+kinds+"}", waterwayAdmin(&JSONHandler{
		Input:  importModel,
		Handle: manualImport,
		NoConn: true,
	})).Methods(http.MethodPost)

	// Import scheduler configuration
	api.Handle("/imports/config/{id:[0-9]+}/run",
		waterwayAdmin(&JSONHandler{
			Handle: runImportConfig,
		})).Methods(http.MethodGet)

	api.Handle("/imports/config/{id:[0-9]+}",
		waterwayAdmin(&JSONHandler{
			Input:  func(*http.Request) interface{} { return &json.RawMessage{} },
			Handle: modifyImportConfig,
		})).Methods(http.MethodPatch)

	api.Handle("/imports/config/{id:[0-9]+}",
		waterwayAdmin(&JSONHandler{
			Handle: deleteImportConfig,
		})).Methods(http.MethodDelete)

	api.Handle("/imports/config/{id:[0-9]+}",
		waterwayAdmin(&JSONHandler{
			Handle: infoImportConfig,
		})).Methods(http.MethodGet)

	api.Handle("/imports/config",
		waterwayAdmin(&JSONHandler{
			Input:  func(*http.Request) interface{} { return new(imports.ImportConfigIn) },
			Handle: addImportConfig,
		})).Methods(http.MethodPost)

	api.Handle("/imports/config",
		waterwayAdmin(&JSONHandler{
			Handle: listImportConfigs,
		})).Methods(http.MethodGet)

	// Import queue
	lsImports := waterwayAdmin(&JSONHandler{
		Handle: listImports,
	})

	api.Handle("/imports", lsImports).
		Methods(http.MethodGet)

	api.Handle("/imports/{id:[0-9]+}", waterwayAdmin(&JSONHandler{
		Handle: importLogs,
	})).Methods(http.MethodGet)

	api.Handle("/imports", waterwayAdmin(&JSONHandler{
		Input:  func(*http.Request) interface{} { return &[]models.Review{} },
		Handle: reviewImports,
	})).Methods(http.MethodPatch)

	api.Handle("/imports/{id:[0-9]+}", waterwayAdmin(&JSONHandler{
		Handle: deleteImport,
	})).Methods(http.MethodDelete)

	// Handler to review an import which is pending.
	api.Handle("/imports/{id:[0-9]+}/{state:(?:accepted|declined)}",
		waterwayAdmin(&JSONHandler{
			Handle: reviewImport,
		})).Methods(http.MethodPut)

	// Handler to serve data to the client.

	api.Handle("/data/{kind:stretch|section}/availability/{name}", any(
		middleware.DBConn(http.HandlerFunc(stretchAvailabilty)))).Methods(http.MethodGet)

	api.Handle("/data/{kind:stretch|section}/fairway-depth/{name}", any(
		middleware.DBConn(http.HandlerFunc(stretchAvailableFairwayDepth)))).Methods(http.MethodGet)

	api.Handle("/data/bottleneck/fairway-depth/{objnam}", any(
		middleware.DBConn(http.HandlerFunc(bottleneckAvailableFairwayDepth)))).Methods(http.MethodGet)

	api.Handle("/data/bottleneck/availability/{objnam}", any(
		middleware.DBConn(http.HandlerFunc(bottleneckAvailabilty)))).Methods(http.MethodGet)

	api.Handle("/data/waterlevels/{gauge}", any(
		middleware.DBConn(http.HandlerFunc(waterlevels)))).Methods(http.MethodGet)

	api.Handle("/data/longterm-waterlevels/{gauge}", any(
		middleware.DBConn(http.HandlerFunc(longtermWaterlevels)))).Methods(http.MethodGet)

	api.Handle("/data/year-waterlevels/{gauge}/{year:[0-9]+}", any(
		middleware.DBConn(http.HandlerFunc(yearWaterlevels)))).Methods(http.MethodGet)

	api.Handle("/data/nash-sutcliffe/{gauge}", any(&JSONHandler{
		Handle: nashSutcliffe,
	})).Methods(http.MethodGet)

	// Token handling: Login/Logout.
	api.HandleFunc("/login", login).
		Methods(http.MethodPost)
	api.Handle("/logout", auth.SessionMiddleware(http.HandlerFunc(logout))).
		Methods(http.MethodGet, http.MethodPost)
	api.Handle("/renew", auth.SessionMiddleware(http.HandlerFunc(renew))).
		Methods(http.MethodGet, http.MethodPost)
}