mirror of
https://github.com/googleapis/genai-toolbox.git
synced 2026-01-09 07:28:05 -05:00
feat(server/admin): add get resource endpoint
This commit is contained in:
@@ -15,6 +15,9 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/render"
|
||||
@@ -28,5 +31,44 @@ func adminRouter(s *Server) (chi.Router, error) {
|
||||
r.Use(middleware.StripSlashes)
|
||||
r.Use(render.SetContentType(render.ContentTypeJSON))
|
||||
|
||||
r.Get("/{resource}", func(w http.ResponseWriter, r *http.Request) { adminGetHandler(s, w, r) })
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// adminGetHandler handles requests for a list of specific resource
|
||||
func adminGetHandler(s *Server, w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
resource := chi.URLParam(r, "resource")
|
||||
|
||||
var resourceList []string
|
||||
switch resource {
|
||||
case "source":
|
||||
sourcesMap := s.ResourceMgr.GetSourcesMap()
|
||||
for n := range sourcesMap {
|
||||
resourceList = append(resourceList, n)
|
||||
}
|
||||
case "authservice":
|
||||
authServicesMap := s.ResourceMgr.GetAuthServiceMap()
|
||||
for n := range authServicesMap {
|
||||
resourceList = append(resourceList, n)
|
||||
}
|
||||
case "tool":
|
||||
toolsMap := s.ResourceMgr.GetToolsMap()
|
||||
for n := range toolsMap {
|
||||
resourceList = append(resourceList, n)
|
||||
}
|
||||
case "toolset":
|
||||
toolsetsMap := s.ResourceMgr.GetToolsetsMap()
|
||||
for n := range toolsetsMap {
|
||||
resourceList = append(resourceList, n)
|
||||
}
|
||||
default:
|
||||
err := fmt.Errorf(`invalid resource %s, please provide one of "source", "authservice", "tool", or "toolset"`, resource)
|
||||
s.logger.DebugContext(ctx, err.Error())
|
||||
_ = render.Render(w, r, newErrResponse(err, http.StatusNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
render.JSON(w, r, resourceList)
|
||||
}
|
||||
|
||||
222
internal/server/admin_test.go
Normal file
222
internal/server/admin_test.go
Normal file
@@ -0,0 +1,222 @@
|
||||
// Copyright 2025 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 server
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"encoding/json"
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
"slices"
|
||||
|
||||
"github.com/googleapis/genai-toolbox/internal/auth"
|
||||
"github.com/googleapis/genai-toolbox/internal/sources"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
var _ sources.Source = &MockSource{}
|
||||
var _ sources.SourceConfig = &MockSourceConfig{}
|
||||
|
||||
// MockSource is used to mock sources in tests
|
||||
type MockSource struct {
|
||||
name string
|
||||
kind string
|
||||
}
|
||||
|
||||
func (s MockSource) SourceKind() string {
|
||||
return s.kind
|
||||
}
|
||||
|
||||
type MockSourceConfig struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
Project string `yaml:"project"`
|
||||
User string `yaml:"user"`
|
||||
Password string `yaml:"password"`
|
||||
Database string `yaml:"database"`
|
||||
}
|
||||
|
||||
func (sc MockSourceConfig) SourceConfigKind() string {
|
||||
return "mock-source"
|
||||
}
|
||||
|
||||
func (sc MockSourceConfig) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) {
|
||||
s := MockSource{
|
||||
name: sc.Name,
|
||||
kind: sc.Kind,
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
var sourceConfig1 = MockSourceConfig{
|
||||
Name: "source1",
|
||||
Kind: "mock-source",
|
||||
Project: "my-project",
|
||||
User: "my-user",
|
||||
Password: "my-password",
|
||||
Database: "my-db",
|
||||
}
|
||||
|
||||
type MockAuthService struct {
|
||||
name string
|
||||
kind string
|
||||
clientID string
|
||||
}
|
||||
|
||||
func (as MockAuthService) AuthServiceKind() string {
|
||||
return as.kind
|
||||
}
|
||||
|
||||
func (as MockAuthService) GetName() string {
|
||||
return as.name
|
||||
}
|
||||
|
||||
func (as MockAuthService) GetClaimsFromHeader(context.Context, http.Header) (map[string]any, error) {
|
||||
return map[string]any{"foo": "bar"}, nil
|
||||
}
|
||||
|
||||
type MockASConfig struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
ClientID string `yaml:"clientId"`
|
||||
}
|
||||
|
||||
func (ac MockASConfig) AuthServiceConfigKind() string {
|
||||
return "mock-auth-service"
|
||||
}
|
||||
|
||||
func (ac MockASConfig) Initialize() (auth.AuthService, error) {
|
||||
a := MockAuthService{
|
||||
name: ac.Name,
|
||||
kind: ac.Kind,
|
||||
clientID: ac.ClientID,
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var authService1 = MockASConfig{
|
||||
Name: "auth-service1",
|
||||
Kind: "mock-auth-service",
|
||||
ClientID: "foo",
|
||||
}
|
||||
|
||||
func TestAdminGetResourceEndpoint(t *testing.T) {
|
||||
source1, _ := sourceConfig1.Initialize(context.Background(), nil)
|
||||
mockSources := []MockSource{source1.(MockSource)}
|
||||
as1, _ := authService1.Initialize()
|
||||
mockAuthServices := []MockAuthService{as1.(MockAuthService)}
|
||||
mockTools := []MockTool{tool1, tool2}
|
||||
sourcesMap, authServicesMap, toolsMap, toolsets := setUpResources(t, mockSources, mockAuthServices, mockTools)
|
||||
r, shutdown := setUpServer(t, "admin", sourcesMap, authServicesMap, toolsMap, toolsets)
|
||||
defer shutdown()
|
||||
ts := runServer(r, false)
|
||||
defer ts.Close()
|
||||
|
||||
// wantResponse is a struct for checks against test cases
|
||||
type wantResponse struct {
|
||||
statusCode int
|
||||
isErr bool
|
||||
errString string
|
||||
resourcesList []string
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
url string
|
||||
want wantResponse
|
||||
}{
|
||||
{
|
||||
name: "get source",
|
||||
url: "/source",
|
||||
want: wantResponse{
|
||||
statusCode: http.StatusOK,
|
||||
resourcesList: []string{"source1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "get auth services",
|
||||
url: "/authservice",
|
||||
want: wantResponse{
|
||||
statusCode: http.StatusOK,
|
||||
resourcesList: []string{"auth-service1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "get tool",
|
||||
url: "/tool",
|
||||
want: wantResponse{
|
||||
statusCode: http.StatusOK,
|
||||
resourcesList: []string{"no_params", "some_params"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "get toolset",
|
||||
url: "/toolset",
|
||||
want: wantResponse{
|
||||
statusCode: http.StatusOK,
|
||||
resourcesList: []string{"", "tool1_only", "tool2_only"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "get invalid",
|
||||
url: "/invalid",
|
||||
want: wantResponse{
|
||||
statusCode: http.StatusNotFound,
|
||||
isErr: true,
|
||||
errString: `invalid resource invalid, please provide one of "source", "authservice", "tool", or "toolset"`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
resp, body, err := runRequest(ts, http.MethodGet, tc.url, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error during request: %s", err)
|
||||
}
|
||||
|
||||
if contentType := resp.Header.Get("Content-type"); contentType != "application/json" {
|
||||
t.Fatalf("unexpected content-type header: want %s, got %s", "application/json", contentType)
|
||||
}
|
||||
|
||||
if tc.want.statusCode != resp.StatusCode {
|
||||
t.Fatalf("unexpected status code: want %d, got %d", tc.want.statusCode, resp.StatusCode)
|
||||
}
|
||||
|
||||
if tc.want.isErr {
|
||||
var res errResponse
|
||||
err = json.Unmarshal(body, &res)
|
||||
if err != nil {
|
||||
t.Fatalf("error unmarshaling body: %s", err)
|
||||
}
|
||||
if tc.want.errString != res.ErrorText {
|
||||
t.Fatalf("unexpected error message: want %s, got %s", tc.want.errString, res.ErrorText)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var res []string
|
||||
err = json.Unmarshal(body, &res)
|
||||
if err != nil {
|
||||
t.Fatalf("error unmarshaling body: %s", err)
|
||||
}
|
||||
slices.Sort(res)
|
||||
if !reflect.DeepEqual(tc.want.resourcesList, res) {
|
||||
t.Fatalf("unexpected response: want %+v, got %+v", tc.want.resourcesList, res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -297,29 +297,3 @@ func (rr resultResponse) Render(w http.ResponseWriter, r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ render.Renderer = &errResponse{} // Renderer interface for managing response payloads.
|
||||
|
||||
// newErrResponse is a helper function initializing an ErrResponse
|
||||
func newErrResponse(err error, code int) *errResponse {
|
||||
return &errResponse{
|
||||
Err: err,
|
||||
HTTPStatusCode: code,
|
||||
|
||||
StatusText: http.StatusText(code),
|
||||
ErrorText: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
// errResponse is the response sent back when an error has been encountered.
|
||||
type errResponse struct {
|
||||
Err error `json:"-"` // low-level runtime error
|
||||
HTTPStatusCode int `json:"-"` // http response status code
|
||||
|
||||
StatusText string `json:"status"` // user-level status message
|
||||
ErrorText string `json:"error,omitempty"` // application-level error message, for debugging
|
||||
}
|
||||
|
||||
func (e *errResponse) Render(w http.ResponseWriter, r *http.Request) error {
|
||||
render.Status(r, e.HTTPStatusCode)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ import (
|
||||
|
||||
func TestToolsetEndpoint(t *testing.T) {
|
||||
mockTools := []MockTool{tool1, tool2}
|
||||
toolsMap, toolsets := setUpResources(t, mockTools)
|
||||
r, shutdown := setUpServer(t, "api", toolsMap, toolsets)
|
||||
sourcesMap, asMap, toolsMap, toolsets := setUpResources(t, nil, nil, mockTools)
|
||||
r, shutdown := setUpServer(t, "api", sourcesMap, asMap, toolsMap, toolsets)
|
||||
defer shutdown()
|
||||
ts := runServer(r, false)
|
||||
defer ts.Close()
|
||||
@@ -125,8 +125,8 @@ func TestToolsetEndpoint(t *testing.T) {
|
||||
|
||||
func TestToolGetEndpoint(t *testing.T) {
|
||||
mockTools := []MockTool{tool1, tool2}
|
||||
toolsMap, toolsets := setUpResources(t, mockTools)
|
||||
r, shutdown := setUpServer(t, "api", toolsMap, toolsets)
|
||||
sourcesMap, asMap, toolsMap, toolsets := setUpResources(t, nil, nil, mockTools)
|
||||
r, shutdown := setUpServer(t, "api", sourcesMap, asMap, toolsMap, toolsets)
|
||||
defer shutdown()
|
||||
ts := runServer(r, false)
|
||||
defer ts.Close()
|
||||
@@ -213,8 +213,8 @@ func TestToolGetEndpoint(t *testing.T) {
|
||||
|
||||
func TestToolInvokeEndpoint(t *testing.T) {
|
||||
mockTools := []MockTool{tool1, tool2, tool4, tool5}
|
||||
toolsMap, toolsets := setUpResources(t, mockTools)
|
||||
r, shutdown := setUpServer(t, "api", toolsMap, toolsets)
|
||||
sourcesMap, asMap, toolsMap, toolsets := setUpResources(t, nil, nil, mockTools)
|
||||
r, shutdown := setUpServer(t, "api", sourcesMap, asMap, toolsMap, toolsets)
|
||||
defer shutdown()
|
||||
ts := runServer(r, false)
|
||||
defer ts.Close()
|
||||
|
||||
@@ -24,7 +24,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/googleapis/genai-toolbox/internal/auth"
|
||||
"github.com/googleapis/genai-toolbox/internal/log"
|
||||
"github.com/googleapis/genai-toolbox/internal/sources"
|
||||
"github.com/googleapis/genai-toolbox/internal/telemetry"
|
||||
"github.com/googleapis/genai-toolbox/internal/tools"
|
||||
)
|
||||
@@ -129,7 +131,17 @@ var tool5 = MockTool{
|
||||
}
|
||||
|
||||
// setUpResources setups resources to test against
|
||||
func setUpResources(t *testing.T, mockTools []MockTool) (map[string]tools.Tool, map[string]tools.Toolset) {
|
||||
func setUpResources(t *testing.T, mockSources []MockSource, mockAuthServices []MockAuthService, mockTools []MockTool) (map[string]sources.Source, map[string]auth.AuthService, map[string]tools.Tool, map[string]tools.Toolset) {
|
||||
sourcesMap := make(map[string]sources.Source)
|
||||
for _, s := range mockSources {
|
||||
sourcesMap[s.name] = s
|
||||
}
|
||||
|
||||
authServicesMap := make(map[string]auth.AuthService)
|
||||
for _, a := range mockAuthServices {
|
||||
authServicesMap[a.name] = a
|
||||
}
|
||||
|
||||
toolsMap := make(map[string]tools.Tool)
|
||||
var allTools []string
|
||||
for _, tool := range mockTools {
|
||||
@@ -151,11 +163,11 @@ func setUpResources(t *testing.T, mockTools []MockTool) (map[string]tools.Tool,
|
||||
}
|
||||
toolsets[name] = m
|
||||
}
|
||||
return toolsMap, toolsets
|
||||
return sourcesMap, authServicesMap, toolsMap, toolsets
|
||||
}
|
||||
|
||||
// setUpServer create a new server with tools and toolsets that are given
|
||||
func setUpServer(t *testing.T, router string, tools map[string]tools.Tool, toolsets map[string]tools.Toolset) (chi.Router, func()) {
|
||||
func setUpServer(t *testing.T, router string, sources map[string]sources.Source, authServices map[string]auth.AuthService, tools map[string]tools.Tool, toolsets map[string]tools.Toolset) (chi.Router, func()) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
testLogger, err := log.NewStdLogger(os.Stdout, os.Stderr, "info")
|
||||
@@ -175,7 +187,7 @@ func setUpServer(t *testing.T, router string, tools map[string]tools.Tool, tools
|
||||
|
||||
sseManager := newSseManager(ctx)
|
||||
|
||||
resourceManager := NewResourceManager(nil, nil, tools, toolsets)
|
||||
resourceManager := NewResourceManager(sources, authServices, tools, toolsets)
|
||||
|
||||
server := Server{
|
||||
version: fakeVersionString,
|
||||
@@ -197,6 +209,11 @@ func setUpServer(t *testing.T, router string, tools map[string]tools.Tool, tools
|
||||
if err != nil {
|
||||
t.Fatalf("unable to initialize mcp router: %s", err)
|
||||
}
|
||||
case "admin":
|
||||
r, err = adminRouter(&server)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to initialize admin router: %s", err)
|
||||
}
|
||||
default:
|
||||
t.Fatalf("unknown router")
|
||||
}
|
||||
|
||||
@@ -27,8 +27,10 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/googleapis/genai-toolbox/internal/auth"
|
||||
"github.com/googleapis/genai-toolbox/internal/log"
|
||||
"github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc"
|
||||
"github.com/googleapis/genai-toolbox/internal/sources"
|
||||
"github.com/googleapis/genai-toolbox/internal/telemetry"
|
||||
"github.com/googleapis/genai-toolbox/internal/tools"
|
||||
)
|
||||
@@ -68,8 +70,8 @@ var tool3InputSchema = map[string]any{
|
||||
|
||||
func TestMcpEndpointWithoutInitialized(t *testing.T) {
|
||||
mockTools := []MockTool{tool1, tool2, tool3, tool4, tool5}
|
||||
toolsMap, toolsets := setUpResources(t, mockTools)
|
||||
r, shutdown := setUpServer(t, "mcp", toolsMap, toolsets)
|
||||
sourcesMap, asMap, toolsMap, toolsets := setUpResources(t, nil, nil, mockTools)
|
||||
r, shutdown := setUpServer(t, "mcp", sourcesMap, asMap, toolsMap, toolsets)
|
||||
defer shutdown()
|
||||
ts := runServer(r, false)
|
||||
defer ts.Close()
|
||||
@@ -337,8 +339,8 @@ func runInitializeLifecycle(t *testing.T, ts *httptest.Server, protocolVersion s
|
||||
|
||||
func TestMcpEndpoint(t *testing.T) {
|
||||
mockTools := []MockTool{tool1, tool2, tool3, tool4, tool5}
|
||||
toolsMap, toolsets := setUpResources(t, mockTools)
|
||||
r, shutdown := setUpServer(t, "mcp", toolsMap, toolsets)
|
||||
sourcesMap, asMap, toolsMap, toolsets := setUpResources(t, nil, nil, mockTools)
|
||||
r, shutdown := setUpServer(t, "mcp", sourcesMap, asMap, toolsMap, toolsets)
|
||||
defer shutdown()
|
||||
ts := runServer(r, false)
|
||||
defer ts.Close()
|
||||
@@ -743,8 +745,8 @@ func TestMcpEndpoint(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInvalidProtocolVersionHeader(t *testing.T) {
|
||||
toolsMap, toolsets := map[string]tools.Tool{}, map[string]tools.Toolset{}
|
||||
r, shutdown := setUpServer(t, "mcp", toolsMap, toolsets)
|
||||
sourcesMap, asMap, toolsMap, toolsets := map[string]sources.Source{}, map[string]auth.AuthService{}, map[string]tools.Tool{}, map[string]tools.Toolset{}
|
||||
r, shutdown := setUpServer(t, "mcp", sourcesMap, asMap, toolsMap, toolsets)
|
||||
defer shutdown()
|
||||
ts := runServer(r, false)
|
||||
defer ts.Close()
|
||||
@@ -770,8 +772,8 @@ func TestInvalidProtocolVersionHeader(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDeleteEndpoint(t *testing.T) {
|
||||
toolsMap, toolsets := map[string]tools.Tool{}, map[string]tools.Toolset{}
|
||||
r, shutdown := setUpServer(t, "mcp", toolsMap, toolsets)
|
||||
sourcesMap, asMap, toolsMap, toolsets := map[string]sources.Source{}, map[string]auth.AuthService{}, map[string]tools.Tool{}, map[string]tools.Toolset{}
|
||||
r, shutdown := setUpServer(t, "mcp", sourcesMap, asMap, toolsMap, toolsets)
|
||||
defer shutdown()
|
||||
ts := runServer(r, false)
|
||||
defer ts.Close()
|
||||
@@ -786,8 +788,8 @@ func TestDeleteEndpoint(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetEndpoint(t *testing.T) {
|
||||
toolsMap, toolsets := map[string]tools.Tool{}, map[string]tools.Toolset{}
|
||||
r, shutdown := setUpServer(t, "mcp", toolsMap, toolsets)
|
||||
sourcesMap, asMap, toolsMap, toolsets := map[string]sources.Source{}, map[string]auth.AuthService{}, map[string]tools.Tool{}, map[string]tools.Toolset{}
|
||||
r, shutdown := setUpServer(t, "mcp", sourcesMap, asMap, toolsMap, toolsets)
|
||||
defer shutdown()
|
||||
ts := runServer(r, false)
|
||||
defer ts.Close()
|
||||
@@ -810,7 +812,7 @@ func TestGetEndpoint(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSseEndpoint(t *testing.T) {
|
||||
r, shutdown := setUpServer(t, "mcp", nil, nil)
|
||||
r, shutdown := setUpServer(t, "mcp", nil, nil, nil, nil)
|
||||
defer shutdown()
|
||||
ts := runServer(r, false)
|
||||
defer ts.Close()
|
||||
@@ -925,7 +927,7 @@ func TestStdioSession(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
mockTools := []MockTool{tool1, tool2, tool3}
|
||||
toolsMap, toolsets := setUpResources(t, mockTools)
|
||||
sourcesMap, asMap, toolsMap, toolsets := setUpResources(t, nil, nil, mockTools)
|
||||
|
||||
pr, pw, err := os.Pipe()
|
||||
if err != nil {
|
||||
@@ -955,7 +957,7 @@ func TestStdioSession(t *testing.T) {
|
||||
|
||||
sseManager := newSseManager(ctx)
|
||||
|
||||
resourceManager := NewResourceManager(nil, nil, toolsMap, toolsets)
|
||||
resourceManager := NewResourceManager(sourcesMap, asMap, toolsMap, toolsets)
|
||||
|
||||
server := &Server{
|
||||
version: fakeVersionString,
|
||||
|
||||
@@ -111,6 +111,12 @@ func (r *ResourceManager) SetResources(sourcesMap map[string]sources.Source, aut
|
||||
r.toolsets = toolsetsMap
|
||||
}
|
||||
|
||||
func (r *ResourceManager) GetSourcesMap() map[string]sources.Source {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
return r.sources
|
||||
}
|
||||
|
||||
func (r *ResourceManager) GetAuthServiceMap() map[string]auth.AuthService {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
@@ -123,6 +129,12 @@ func (r *ResourceManager) GetToolsMap() map[string]tools.Tool {
|
||||
return r.tools
|
||||
}
|
||||
|
||||
func (r *ResourceManager) GetToolsetsMap() map[string]tools.Toolset {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
return r.toolsets
|
||||
}
|
||||
|
||||
func InitializeConfigs(ctx context.Context, cfg ServerConfig) (
|
||||
map[string]sources.Source,
|
||||
map[string]auth.AuthService,
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -152,6 +153,26 @@ func TestUpdateServer(t *testing.T) {
|
||||
t.Errorf("error updating server: %s", err)
|
||||
}
|
||||
|
||||
gotSourcesMap := s.ResourceMgr.GetSourcesMap()
|
||||
if !reflect.DeepEqual(gotSourcesMap, newSources) {
|
||||
t.Errorf("error retrieving sources map: got %+v, want %+v", gotSourcesMap, newSources)
|
||||
}
|
||||
|
||||
gotAuthServiceMap := s.ResourceMgr.GetAuthServiceMap()
|
||||
if !reflect.DeepEqual(gotAuthServiceMap, newAuth) {
|
||||
t.Errorf("error retrieving auth servies map: got %+v, want %+v", gotAuthServiceMap, newAuth)
|
||||
}
|
||||
|
||||
gotToolsMap := s.ResourceMgr.GetToolsMap()
|
||||
if !reflect.DeepEqual(gotToolsMap, newTools) {
|
||||
t.Errorf("error retrieving tools map: got %+v, want %+v", gotToolsMap, newTools)
|
||||
}
|
||||
|
||||
gotToolsetsMap := s.ResourceMgr.GetToolsetsMap()
|
||||
if !reflect.DeepEqual(gotToolsetsMap, newToolsets) {
|
||||
t.Errorf("error retrieving toolsets map: got %+v, want %+v", gotToolsetsMap, newToolsets)
|
||||
}
|
||||
|
||||
gotSource, _ := s.ResourceMgr.GetSource("example-source")
|
||||
if diff := cmp.Diff(gotSource, newSources["example-source"]); diff != "" {
|
||||
t.Errorf("error updating server, sources (-want +got):\n%s", diff)
|
||||
|
||||
48
internal/server/util.go
Normal file
48
internal/server/util.go
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright 2025 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 server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
var _ render.Renderer = &errResponse{} // Renderer interface for managing response payloads.
|
||||
|
||||
// newErrResponse is a helper function initializing an ErrResponse
|
||||
func newErrResponse(err error, code int) *errResponse {
|
||||
return &errResponse{
|
||||
Err: err,
|
||||
HTTPStatusCode: code,
|
||||
|
||||
StatusText: http.StatusText(code),
|
||||
ErrorText: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
// errResponse is the response sent back when an error has been encountered.
|
||||
type errResponse struct {
|
||||
Err error `json:"-"` // low-level runtime error
|
||||
HTTPStatusCode int `json:"-"` // http response status code
|
||||
|
||||
StatusText string `json:"status"` // user-level status message
|
||||
ErrorText string `json:"error,omitempty"` // application-level error message, for debugging
|
||||
}
|
||||
|
||||
func (e *errResponse) Render(w http.ResponseWriter, r *http.Request) error {
|
||||
render.Status(r, e.HTTPStatusCode)
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user