Skip to content
This repository has been archived by the owner on Jul 12, 2023. It is now read-only.

Add export importers to the admin console #1085

Merged
merged 4 commits into from
Oct 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion internal/admin/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ func (c *Config) RenderTemplate(w http.ResponseWriter, tmpl string, p TemplateMa
fmt.Sprintf("%s/%s.html", c.TemplatePath, c.BotFile),
}

t, err := template.ParseFiles(files...)
t, err := template.
New(tmpl).
Funcs(TemplateFuncMap).
ParseFiles(files...)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, err)
Expand Down
15 changes: 15 additions & 0 deletions internal/admin/controller.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, softwar
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package admin

import (
Expand All @@ -16,4 +30,5 @@ type Controller interface {
func ErrorPage(c *gin.Context, messages ...string) {
log.Printf("error: %v", messages)
c.HTML(http.StatusOK, "error", gin.H{"error": messages})
c.Abort()
}
70 changes: 70 additions & 0 deletions internal/admin/exportimporters/form.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, softwar
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package exportimporters is part of the admin system.
package exportimporters

import (
"github.com/google/exposure-notifications-server/internal/admin"
"github.com/google/exposure-notifications-server/internal/exportimport/model"
)

type formData struct {
IndexFile string `form:"index_file"`
ExportRoot string `form:"export_root"`
Region string `form:"region"`

// FromDate and FromTime are combined into FromTimestamp.
FromDate string `form:"from_date"`
FromTime string `form:"from_time"`

// ThruDate and ThruTime are combined into ThruTimestamp.
ThruDate string `form:"thru_date"`
ThruTime string `form:"thru_time"`
}

// BuildExportImporterModel populates and mutates the given model with form
// data. It overwrites any form data that's present.
func (f *formData) BuildExportImporterModel(c *model.ExportImport) error {
from, err := admin.CombineDateAndTime(f.FromDate, f.FromTime)
if err != nil {
return err
}
thru, err := admin.CombineDateAndTime(f.ThruDate, f.ThruTime)
if err != nil {
return err
}

if val := f.IndexFile; val != "" {
c.IndexFile = val
}

if val := f.ExportRoot; val != "" {
c.ExportRoot = val
}

if val := f.Region; val != "" {
c.Region = val
}

if !from.IsZero() {
c.From = from
}

if !thru.IsZero() {
c.Thru = &thru
}

return nil
}
85 changes: 85 additions & 0 deletions internal/admin/exportimporters/save.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, softwar
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package exportimporters

import (
"fmt"
"net/http"
"strconv"

"github.com/gin-gonic/gin"
"github.com/google/exposure-notifications-server/internal/admin"
exportimportdatabase "github.com/google/exposure-notifications-server/internal/exportimport/database"
exportimportmodel "github.com/google/exposure-notifications-server/internal/exportimport/model"
"github.com/google/exposure-notifications-server/internal/serverenv"
)

type saveController struct {
config *admin.Config
env *serverenv.ServerEnv
}

func NewSave(c *admin.Config, env *serverenv.ServerEnv) admin.Controller {
return &saveController{config: c, env: env}
}

func (v *saveController) Execute(c *gin.Context) {
var form formData
if err := c.Bind(&form); err != nil {
admin.ErrorPage(c, err.Error())
return
}

ctx := c.Request.Context()
m := admin.TemplateMap{}

db := exportimportdatabase.New(v.env.Database())
model := new(exportimportmodel.ExportImport)

idRaw := c.Param("id")
if idRaw != "" && idRaw != "0" {
id, err := strconv.ParseInt(idRaw, 10, 64)
if err != nil {
admin.ErrorPage(c, "failed to to parse `id` param.")
return
}

model, err = db.GetConfig(ctx, id)
if err != nil {
admin.ErrorPage(c, fmt.Sprintf("failed to load export importer config: %s", err))
return
}
}

if err := form.BuildExportImporterModel(model); err != nil {
admin.ErrorPage(c, fmt.Sprintf("failed to build export importer config: %s", err))
return
}

fn := db.AddConfig
if model.ID != 0 {
fn = db.UpdateConfig
}

if err := fn(ctx, model); err != nil {
admin.ErrorPage(c, fmt.Sprintf("failed to write export importer config: %s", err))
return
}

m.AddSuccess("Successfully updated export importer config!")
m["model"] = model
c.HTML(http.StatusOK, "export-importer", m)
c.Abort()
}
62 changes: 62 additions & 0 deletions internal/admin/exportimporters/view.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, softwar
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package exportimporters

import (
"fmt"
"net/http"
"strconv"

"github.com/gin-gonic/gin"
"github.com/google/exposure-notifications-server/internal/admin"
exportimportdatabase "github.com/google/exposure-notifications-server/internal/exportimport/database"
exportimportmodel "github.com/google/exposure-notifications-server/internal/exportimport/model"
"github.com/google/exposure-notifications-server/internal/serverenv"
)

type viewController struct {
config *admin.Config
env *serverenv.ServerEnv
}

func NewView(c *admin.Config, env *serverenv.ServerEnv) admin.Controller {
return &viewController{config: c, env: env}
}

func (v *viewController) Execute(c *gin.Context) {
ctx := c.Request.Context()

db := exportimportdatabase.New(v.env.Database())
model := new(exportimportmodel.ExportImport)

if idRaw := c.Param("id"); idRaw != "" && idRaw != "0" {
id, err := strconv.ParseInt(idRaw, 10, 64)
if err != nil {
admin.ErrorPage(c, fmt.Sprintf("Failed to parse `id` param: %s", err))
return
}

model, err = db.GetConfig(ctx, id)
if err != nil {
admin.ErrorPage(c, fmt.Sprintf("Failed to load export importer config: %s", err))
return
}
}

m := make(admin.TemplateMap)
m["model"] = model
c.HTML(http.StatusOK, "export-importer", m)
c.Abort()
}
46 changes: 46 additions & 0 deletions internal/admin/exportimporters/view_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, softwar
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package exportimporters

import (
"net/http/httptest"
"testing"

"github.com/google/exposure-notifications-server/internal/admin"
exportimportmodel "github.com/google/exposure-notifications-server/internal/exportimport/model"
)

func TestRenderSignatureInfo(t *testing.T) {
// Hello developer!
// If this test fails, it's likely that you changed something in
// internal/authorizedapp/model/
// And whatever you changed is used in the
// tools/admin-console/templates/siginfo.html
// That is what caused the test failure.
m := admin.TemplateMap{}
model := new(exportimportmodel.ExportImport)
m["model"] = model

recorder := httptest.NewRecorder()
config := admin.Config{
TemplatePath: "../../../tools/admin-console/templates",
TopFile: "top",
BotFile: "bottom",
}
err := config.RenderTemplate(recorder, "export-importer", m)
if err != nil {
t.Fatalf("error rendering template: %v", err)
}
}
22 changes: 15 additions & 7 deletions internal/admin/index/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/google/exposure-notifications-server/internal/admin"
aadb "github.com/google/exposure-notifications-server/internal/authorizedapp/database"
exdb "github.com/google/exposure-notifications-server/internal/export/database"
exportimportdatabase "github.com/google/exposure-notifications-server/internal/exportimport/database"
"github.com/google/exposure-notifications-server/internal/serverenv"
hadb "github.com/google/exposure-notifications-server/internal/verification/database"
)
Expand All @@ -39,35 +40,42 @@ func (h *indexHandler) Execute(c *gin.Context) {
ctx := c.Request.Context()
m := admin.TemplateMap{}

db := h.env.Database()

// Load authorized apps for index.
db := aadb.New(h.env.Database())
apps, err := db.ListAuthorizedApps(ctx)
apps, err := aadb.New(db).ListAuthorizedApps(ctx)
if err != nil {
admin.ErrorPage(c, err.Error())
return
}
m["apps"] = apps

// Load health authorities.
haDB := hadb.New(h.env.Database())
has, err := haDB.ListAllHealthAuthoritiesWithoutKeys(ctx)
has, err := hadb.New(db).ListAllHealthAuthoritiesWithoutKeys(ctx)
if err != nil {
admin.ErrorPage(c, err.Error())
return
}
m["healthauthorities"] = has

// Load export configurations.
exportDB := exdb.New(h.env.Database())
exports, err := exportDB.GetAllExportConfigs(ctx)
exports, err := exdb.New(db).GetAllExportConfigs(ctx)
if err != nil {
admin.ErrorPage(c, err.Error())
return
}
m["exports"] = exports

// Load export importer configurations.
exportImporters, err := exportimportdatabase.New(db).ListConfigs(ctx)
if err != nil {
admin.ErrorPage(c, err.Error())
return
}
m["exportImporters"] = exportImporters

// Load SignatureInfos
sigInfos, err := exportDB.ListAllSigntureInfos(ctx)
sigInfos, err := exdb.New(db).ListAllSigntureInfos(ctx)
if err != nil {
admin.ErrorPage(c, err.Error())
return
Expand Down
Loading