package main
import (
"device-analytics/configuration"
"device-analytics/data"
"device-analytics/handlers"
"device-analytics/logic"
"flag"
"fmt"
"net/http"
"os"
"path"
log "gerrit.wikimedia.org/r/mediawiki/services/servicelib-golang/logger"
prometheusmiddleware "github.com/albertogviana/prometheus-middleware"
"github.com/gorilla/mux"
"github.com/prometheus/client_golang/prometheus/promhttp"
"gitlab.wikimedia.org/repos/generated-data-platform/aqs/aqsassist"
)
var (
// These values are assigned at build using `-ldflags` (see: Makefile)
buildDate = "unknown"
buildHost = "unknown"
version = "unknown"
)
// API documentation
// @title Wikimedia Device Analytics
// @version 1
// @description Device Analytics provides data about the number of unique devices that access Wikimedia projects.
// @description Data provided by this API is available under the [CC0 1.0 license](https://creativecommons.org/publicdomain/zero/1.0/).
// @termsOfService https://foundation.wikimedia.org/wiki/Special:MyLanguage/Policy:Terms_of_Use
// @host wikimedia.org
// @basePath /api/rest_v1/metrics
// @schemes https
// Entrypoint for the service
func main() {
var confFile = flag.String("config", "./config.yaml", aqsassist.ConfigurationPathMessage)
var config *configuration.Config
var err error
var uniqueDevicesData data.UniqueDevicesData
var uniqueDeviceslogic = logic.NewUniqueDevicesLogic(uniqueDevicesData)
flag.Parse()
if config, err = configuration.ReadConfig(*confFile); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
logger, err := log.NewLogger(os.Stdout, config.ServiceName, config.LogLevel)
if err != nil {
fmt.Fprintf(os.Stderr, aqsassist.ErrorInitializingLoggerMessage, err)
os.Exit(1)
}
// Allow overriding config using environment variables
MergeEnvironment(config, logger)
aqsassist.LogCassandraServiceInitialization(logger, config.ServiceName, version, buildHost, buildDate, config.Cassandra.Hosts,
config.Cassandra.Port, config.Cassandra.Consistency, config.Cassandra.LocalDC)
session, err := newCassandraSession(config)
if err != nil {
logger.Error(aqsassist.CassandraConnectionErrorMessage, err)
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
// pass bound struct method to handler
uniqueDevicesHandler := &handlers.UniqueDevicesHandler{
Logger: logger, Session: session, Logic: &uniqueDeviceslogic, Config: config}
apiSpecHandler := &handlers.ApiSpecHandler{
Logger: logger}
healthz := NewHealthz(version, buildDate, buildHost)
middleware := prometheusmiddleware.NewPrometheusMiddleware(prometheusmiddleware.Opts{})
r := mux.NewRouter().SkipClean(true).UseEncodedPath()
r.NotFoundHandler = http.HandlerFunc(handlers.NotFoundHandler)
p := promhttp.Handler()
r.Use(SetSecurityHeaders, ValidateProject, middleware.InstrumentHandlerDuration)
r.Handle("/metrics", p).Methods("GET")
r.HandleFunc("/healthz", healthz.HandleHTTP).Methods("GET")
r.HandleFunc("/device-analytics/api-spec.json", apiSpecHandler.HandleHTTP).Methods("GET")
r.HandleFunc(path.Join(config.BaseURI, "/{project}/{access-site}/{granularity}/{start}/{end}"), uniqueDevicesHandler.HandleHTTP).Methods("GET")
srv := &http.Server{
Addr: fmt.Sprintf("%s:%d", config.Address, config.Port),
Handler: r,
}
err = srv.ListenAndServe()
fmt.Println(err.Error())
}