func (s Server) ListDashboards(w http.ResponseWriter, r *http.Request) {
	cfg := s.getConfig(w, r)
	if cfg == nil {
		return
	}
	var dashboardResponse apipb.ListDashboardResponse
	for _, dashboard := range cfg.Dashboards {
		rsc := apipb.Resource{
			Name: dashboard.Name,
			Link: fmt.Sprintf("%s/dashboards/%s%s", s.Host.String(), config.Normalize(dashboard.Name), passQueryParameters(r)),
		}
		dashboardResponse.Dashboards = append(dashboardResponse.Dashboards, &rsc)
	}
	writeJSON(w, &dashboardResponse)
}
ListDashboards returns every dashboard in TestGrid
Response json: ListDashboardResponse
package v1
import (
	"errors"
	"fmt"
	"net/http"
	"github.com/gorilla/mux"
	"github.com/sirupsen/logrus"
	"github.com/GoogleCloudPlatform/testgrid/config"
	apipb "github.com/GoogleCloudPlatform/testgrid/pb/api/v1"
	configpb "github.com/GoogleCloudPlatform/testgrid/pb/config"
	"github.com/GoogleCloudPlatform/testgrid/util/gcs"
)
func (s Server) configPath(r *http.Request) (path *gcs.Path, isDefault bool, err error) {
	scope := r.URL.Query().Get("scope")
	if scope != "" {
		path, err = gcs.NewPath(fmt.Sprintf("%s/%s", scope, "config"))
		isDefault = false
		return
	}
	if s.DefaultBucket != "" {
		path, err = gcs.NewPath(fmt.Sprintf("%s/%s", s.DefaultBucket, "config"))
		isDefault = true
		return
	}
	return nil, false, errors.New("no testgrid scope")
}
func passQueryParameters(r *http.Request) string {
	if scope := r.URL.Query().Get("scope"); scope != "" {
		return fmt.Sprintf("?scope=%s", scope)
	}
	return ""
}
func (s Server) getConfig(w http.ResponseWriter, r *http.Request) *configpb.Configuration {
	configPath, isDefault, err := s.configPath(r)
	if err != nil || configPath == nil {
		http.Error(w, "Scope not specified", http.StatusBadRequest)
		return nil
	}
	cfg, err := config.ReadGCS(r.Context(), s.Client, *configPath)
	if err != nil {
		http.Error(w, fmt.Sprintf("Could not read config at %q", configPath.String()), http.StatusInternalServerError)
		if isDefault {
			logrus.WithError(err).Errorf("Can't read default config at %q; check permissions", configPath.String())
		}
		return nil
	}
	return cfg
}
func (s Server) ListDashboardGroups(w http.ResponseWriter, r *http.Request) {
	cfg := s.getConfig(w, r)
	if cfg == nil {
		return
	}
	var groups apipb.ListDashboardGroupResponse
	for _, group := range cfg.DashboardGroups {
		rsc := apipb.Resource{
			Name: group.Name,
			Link: fmt.Sprintf("%s/dashboard-groups/%s%s", s.Host.String(), config.Normalize(group.Name), passQueryParameters(r)),
		}
		groups.DashboardGroups = append(groups.DashboardGroups, &rsc)
	}
	writeJSON(w, &groups)
}
func (s Server) GetDashboardGroup(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	cfg := s.getConfig(w, r)
	if cfg == nil {
		return
	}
	dashboardGroupKey := vars["dashboard-group"]
	for _, group := range cfg.DashboardGroups {
		if config.Normalize(group.Name) == dashboardGroupKey {
			result := apipb.GetDashboardGroupResponse{}
			for _, dash := range group.DashboardNames {
				rsc := apipb.Resource{
					Name: dash,
					Link: fmt.Sprintf("%s/dashboards/%s%s", s.Host.String(), config.Normalize(dash), passQueryParameters(r)),
				}
				result.Dashboards = append(result.Dashboards, &rsc)
			}
			writeJSON(w, &result)
			return
		}
	}
	http.Error(w, fmt.Sprintf("Dashboard group %q not found", vars["dashboard-group"]), http.StatusNotFound)
}