lib/storage/util.go (126 lines of code) (raw):
// Copyright 2019 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, software
// 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 storage
import (
"fmt"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"github.com/golang/protobuf/jsonpb" /* copybara-comment */
"github.com/golang/protobuf/proto" /* copybara-comment */
cpb "github.com/GoogleCloudPlatform/healthcare-federated-access-services/proto/common/v1" /* copybara-comment: go_proto */
)
var (
secretParams = map[string]bool{
"clientSecret": true,
"client_secret": true,
"code": true,
"link_token": true,
"redirect_uri": true,
}
)
func MakeConfigHistory(desc, resType string, rev int64, ts float64, r *http.Request, user string, orig, update proto.Message) proto.Message {
path := ""
query := ""
method := ""
if r != nil {
path = r.URL.Path
method = r.Method
first := true
for name, values := range r.URL.Query() {
if first {
first = false
} else {
query += "&"
}
value := strings.Join(values, ",")
if _, ok := secretParams[name]; ok {
value = "***"
}
query += url.QueryEscape(name) + "=" + url.QueryEscape(value)
}
}
m := jsonpb.Marshaler{}
ov, _ := m.MarshalToString(orig)
uv, _ := m.MarshalToString(update)
return &cpb.HistoryEntry{
Revision: rev,
User: user,
CommitTime: ts,
Path: path,
Query: query,
Desc: desc,
Method: method,
ChangeType: resType,
OriginalValue: ov,
ChangeRequest: uv,
}
}
// GetHistory returns a History object based on the input parameters.
func GetHistory(store Store, datatype, realm, user, id string, r *http.Request) (*cpb.History, int, error) {
hlist := make([]proto.Message, 0)
if err := store.ReadHistory(datatype, realm, user, id, &hlist); err != nil {
if os.IsNotExist(err) {
// TODO: perhaps this should be the empty list?
return nil, http.StatusBadRequest, fmt.Errorf("no config history available")
}
return nil, http.StatusBadRequest, fmt.Errorf("service storage unavailable: %v, retry later", err)
}
he := make([]*cpb.HistoryEntry, len(hlist))
var ok bool
for i, e := range hlist {
he[i], ok = e.(*cpb.HistoryEntry)
if !ok {
return nil, http.StatusInternalServerError, fmt.Errorf("cannot load history entry %d", i)
}
}
history := &cpb.History{
History: he,
}
pageToken := r.URL.Query().Get("pageToken")
start, err := strconv.ParseInt(pageToken, 10, 64)
if err != nil {
start = 0
}
pageSize := r.URL.Query().Get("pageSize")
size, err := strconv.ParseInt(pageSize, 10, 64)
if err != nil || size < 1 {
size = 50
}
if size > 1000 {
size = 1000
}
// Reverse order
a := history.History
for i := len(a)/2 - 1; i >= 0; i-- {
opp := len(a) - 1 - i
a[i], a[opp] = a[opp], a[i]
}
for i, entry := range history.History {
if entry.Revision <= start {
history.History = history.History[i:]
break
}
}
if len(history.History) > int(size) {
history.NextPageToken = fmt.Sprintf("%d", history.History[size].Revision)
history.History = history.History[:size]
}
return history, http.StatusOK, nil
}
// ReplaceContentVariables does simple string replacement of variable names to values.
// If varValues is nil, then it only checks that `content` is JSON.
func ReplaceContentVariables(content proto.Message, varValues map[string]string) error {
m := jsonpb.Marshaler{}
js, err := m.MarshalToString(content)
if err != nil {
return err
}
replace := 0
for k, v := range varValues {
js = strings.Replace(js, k, v, -1)
replace++
}
if replace == 0 {
return nil
}
if err := jsonpb.Unmarshal(strings.NewReader(js), content); err != nil {
return err
}
return nil
}