diff --git a/grafana-plugin/.bra.toml b/grafana-plugin/.bra.toml index 24d10b6d..eaa76a4d 100644 --- a/grafana-plugin/.bra.toml +++ b/grafana-plugin/.bra.toml @@ -4,19 +4,14 @@ [run] init_cmds = [ ["mage", "-v", "build:debug"], - ["mage", "-v" , "reloadPlugin"], ] watch_all = true follow_symlinks = false ignore = [".git", "node_modules", "dist"] ignore_files = ["mage_output_file.go"] -watch_dirs = [ - "pkg", - # "src", -] +watch_dirs = ["pkg"] watch_exts = [".go", ".json"] build_delay = 2000 cmds = [ ["mage", "-v", "build:debug"], - ["mage", "-v" , "reloadPlugin"], ] diff --git a/grafana-plugin/go.mod b/grafana-plugin/go.mod index f059ec75..aecbb332 100644 --- a/grafana-plugin/go.mod +++ b/grafana-plugin/go.mod @@ -1,4 +1,4 @@ -module github.com/grafana-labs/grafana-oncall-app +module github.com/grafana/grafana-oncall-app go 1.21.5 diff --git a/grafana-plugin/pkg/main.go b/grafana-plugin/pkg/main.go index 2d956202..948e5f08 100644 --- a/grafana-plugin/pkg/main.go +++ b/grafana-plugin/pkg/main.go @@ -3,7 +3,7 @@ package main import ( "os" - "github.com/grafana-labs/grafana-oncall-app/pkg/plugin" + "github.com/grafana/grafana-oncall-app/pkg/plugin" "github.com/grafana/grafana-plugin-sdk-go/backend/app" "github.com/grafana/grafana-plugin-sdk-go/backend/log" ) @@ -16,7 +16,7 @@ func main() { // argument. This factory will be automatically called on incoming request // from Grafana to create different instances of `App` (per plugin // ID). - if err := app.Manage("grafana-oncall-app", plugin.NewApp, app.ManageOpts{}); err != nil { + if err := app.Manage("grafana-oncall-app", plugin.NewInstance, app.ManageOpts{}); err != nil { log.DefaultLogger.Error(err.Error()) os.Exit(1) } diff --git a/grafana-plugin/pkg/plugin/app.go b/grafana-plugin/pkg/plugin/app.go index cd51cd4a..84e03b93 100644 --- a/grafana-plugin/pkg/plugin/app.go +++ b/grafana-plugin/pkg/plugin/app.go @@ -38,15 +38,9 @@ type App struct { } // NewApp creates a new example *App instance. -func NewApp(ctx context.Context, settings backend.AppInstanceSettings) (instancemgmt.Instance, error) { +func NewApp(ctx context.Context, settings backend.AppInstanceSettings) (*App, error) { var app App - // Use a httpadapter (provided by the SDK) for resource calls. This allows us - // to use a *http.ServeMux for resource calls, so we can map multiple routes - // to CallResource without having to implement extra logic. - mux := http.NewServeMux() - app.registerRoutes(mux) - app.CallResourceHandler = httpadapter.New(mux) app.OnCallSyncCache = &OnCallSyncCache{} app.OnCallSettingsCache = &OnCallSettingsCache{} app.OnCallUserCache = NewOnCallUserCache() @@ -66,6 +60,25 @@ func NewApp(ctx context.Context, settings backend.AppInstanceSettings) (instance return &app, nil } +// NewInstance creates a new example *Instance instance. +func NewInstance(ctx context.Context, settings backend.AppInstanceSettings) (instancemgmt.Instance, error) { + app, err := NewApp(ctx, settings) + + if err != nil { + log.DefaultLogger.Error("Error creating new app", "error", err) + return nil, err + } + + // Use a httpadapter (provided by the SDK) for resource calls. This allows us + // to use a *http.ServeMux for resource calls, so we can map multiple routes + // to CallResource without having to implement extra logic. + mux := http.NewServeMux() + app.registerRoutes(mux) + app.CallResourceHandler = httpadapter.New(mux) + + return app, nil +} + // Dispose here tells plugin SDK that plugin wants to clean up resources when a new instance // created. func (a *App) Dispose() { diff --git a/grafana-plugin/pkg/plugin/debug.go b/grafana-plugin/pkg/plugin/debug.go index e362c9cc..7c429d44 100644 --- a/grafana-plugin/pkg/plugin/debug.go +++ b/grafana-plugin/pkg/plugin/debug.go @@ -2,9 +2,10 @@ package plugin import ( "encoding/json" + "net/http" + "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter" - "net/http" ) type OnCallDebugStats struct { @@ -47,7 +48,7 @@ func (a *App) handleDebugSync(w http.ResponseWriter, req *http.Request) { return } - onCallSync, err := a.GetSyncData(req.Context(), onCallPluginSettings) + onCallSync, err := a.GetSyncData(onCallPluginSettings) if err != nil { log.DefaultLogger.Error("Error getting sync data", "error", err) return diff --git a/grafana-plugin/pkg/plugin/install.go b/grafana-plugin/pkg/plugin/install.go index da6f94b2..6f4d108d 100644 --- a/grafana-plugin/pkg/plugin/install.go +++ b/grafana-plugin/pkg/plugin/install.go @@ -41,7 +41,7 @@ func (a *App) handleInstall(w http.ResponseWriter, req *http.Request) { return } - onCallSync, err := a.GetSyncData(req.Context(), onCallPluginSettings) + onCallSync, err := a.GetSyncData(onCallPluginSettings) if err != nil { log.DefaultLogger.Error("Error getting sync data", "error", err) return diff --git a/grafana-plugin/pkg/plugin/resources.go b/grafana-plugin/pkg/plugin/resources.go index 7dad9564..b4342084 100644 --- a/grafana-plugin/pkg/plugin/resources.go +++ b/grafana-plugin/pkg/plugin/resources.go @@ -84,7 +84,7 @@ func afterRequest(handler http.Handler, afterFunc func(*responseWriter, *http.Re }) } -func (a *App) handleInternalApi(w http.ResponseWriter, req *http.Request) { +func (a *App) HandleInternalApi(w http.ResponseWriter, req *http.Request) { a.ProxyRequestToOnCall(w, req, "api/internal/v1/") } @@ -121,10 +121,10 @@ func (a *App) handleLegacyInstall(w *responseWriter, req *http.Request) { // registerRoutes takes a *http.ServeMux and registers some HTTP handlers. func (a *App) registerRoutes(mux *http.ServeMux) { mux.HandleFunc("/plugin/install", a.handleInstall) - mux.HandleFunc("/plugin/status", a.handleStatus) - mux.HandleFunc("/plugin/sync", a.handleSync) + mux.HandleFunc("/plugin/status", a.HandleStatus) + mux.HandleFunc("/plugin/sync", a.HandleSync) - mux.Handle("/plugin/self-hosted/install", afterRequest(http.HandlerFunc(a.handleInternalApi), a.handleLegacyInstall)) + mux.Handle("/plugin/self-hosted/install", afterRequest(http.HandlerFunc(a.HandleInternalApi), a.handleLegacyInstall)) // Disable debug endpoints //mux.HandleFunc("/debug/user", a.handleDebugUser) @@ -134,5 +134,5 @@ func (a *App) registerRoutes(mux *http.ServeMux) { //mux.HandleFunc("/debug/stats", a.handleDebugStats) //mux.HandleFunc("/debug/unlock", a.handleDebugUnlock) - mux.HandleFunc("/", a.handleInternalApi) + mux.HandleFunc("/", a.HandleInternalApi) } diff --git a/grafana-plugin/pkg/plugin/resources_test.go b/grafana-plugin/pkg/plugin/resources_test.go index 7506326a..e3a88e81 100644 --- a/grafana-plugin/pkg/plugin/resources_test.go +++ b/grafana-plugin/pkg/plugin/resources_test.go @@ -3,8 +3,9 @@ package plugin import ( "bytes" "context" - "github.com/grafana/grafana-plugin-sdk-go/backend" "testing" + + "github.com/grafana/grafana-plugin-sdk-go/backend" ) // mockCallResourceResponseSender implements backend.CallResourceResponseSender @@ -23,7 +24,7 @@ func (s *mockCallResourceResponseSender) Send(response *backend.CallResourceResp // This ensures the httpadapter for CallResource works correctly. func TestCallResource(t *testing.T) { // Initialize app - inst, err := NewApp(context.Background(), backend.AppInstanceSettings{}) + inst, err := NewInstance(context.Background(), backend.AppInstanceSettings{}) if err != nil { t.Fatalf("new app: %s", err) } diff --git a/grafana-plugin/pkg/plugin/settings.go b/grafana-plugin/pkg/plugin/settings.go index 700bc423..8386c7db 100644 --- a/grafana-plugin/pkg/plugin/settings.go +++ b/grafana-plugin/pkg/plugin/settings.go @@ -317,32 +317,29 @@ func (a *App) SaveOnCallSettings(settings *OnCallPluginSettings) error { return nil } -func (a *App) GetSyncData(ctx context.Context, settings *OnCallPluginSettings) (*OnCallSync, error) { +func (a *App) GetSyncData(pluginSettings *OnCallPluginSettings) (*OnCallSync, error) { + var err error + startGetSyncData := time.Now() defer func() { elapsed := time.Since(startGetSyncData) log.DefaultLogger.Info("GetSyncData", "time", elapsed.Milliseconds()) }() - onCallPluginSettings, err := a.OnCallSettingsFromContext(ctx) - if err != nil { - return nil, fmt.Errorf("error getting settings from context = %v", err) - } - onCallSync := OnCallSync{ - Settings: *settings, + Settings: *pluginSettings, } - onCallSync.Users, err = a.GetAllUsersWithPermissions(onCallPluginSettings) + onCallSync.Users, err = a.GetAllUsersWithPermissions(pluginSettings) if err != nil { return nil, fmt.Errorf("error getting users = %v", err) } - onCallSync.Teams, err = a.GetAllTeams(onCallPluginSettings) + onCallSync.Teams, err = a.GetAllTeams(pluginSettings) if err != nil { return nil, fmt.Errorf("error getting teams = %v", err) } - teamMembers, err := a.GetAllTeamMembers(onCallPluginSettings, onCallSync.Teams) + teamMembers, err := a.GetAllTeamMembers(pluginSettings, onCallSync.Teams) if err != nil { return nil, fmt.Errorf("error getting team members = %v", err) } diff --git a/grafana-plugin/pkg/plugin/status.go b/grafana-plugin/pkg/plugin/status.go index 935e9bf3..ab93a742 100644 --- a/grafana-plugin/pkg/plugin/status.go +++ b/grafana-plugin/pkg/plugin/status.go @@ -244,7 +244,7 @@ func (a *App) ValidateOnCallStatus(ctx context.Context, settings *OnCallPluginSe return &status, nil } -func (a *App) handleStatus(w http.ResponseWriter, req *http.Request) { +func (a *App) HandleStatus(w http.ResponseWriter, req *http.Request) { if req.Method != http.MethodGet { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return diff --git a/grafana-plugin/pkg/plugin/sync.go b/grafana-plugin/pkg/plugin/sync.go index 72a1c6f6..d79e22fa 100644 --- a/grafana-plugin/pkg/plugin/sync.go +++ b/grafana-plugin/pkg/plugin/sync.go @@ -7,13 +7,14 @@ import ( "encoding/json" "errors" "fmt" - "github.com/grafana/grafana-plugin-sdk-go/backend/log" "io" "net/http" "net/url" "strconv" "sync" "time" + + "github.com/grafana/grafana-plugin-sdk-go/backend/log" ) type OnCallSyncCache struct { @@ -38,7 +39,7 @@ func (oc *OnCallSyncCache) UnlockAfterDelay(delay time.Duration) { }) } -func (a *App) handleSync(w http.ResponseWriter, req *http.Request) { +func (a *App) HandleSync(w http.ResponseWriter, req *http.Request) { if req.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return @@ -122,7 +123,7 @@ func (a *App) makeSyncRequest(ctx context.Context, forceSend bool) error { return fmt.Errorf("error getting settings from context: %v ", err) } - onCallSync, err := a.GetSyncData(ctx, onCallPluginSettings) + onCallSync, err := a.GetSyncData(onCallPluginSettings) if err != nil { return fmt.Errorf("error getting sync data: %v", err) } diff --git a/grafana-plugin/src/network/grafana-api/http-client.ts b/grafana-plugin/src/network/grafana-api/http-client.ts index 44a9ead3..d0ff66f4 100644 --- a/grafana-plugin/src/network/grafana-api/http-client.ts +++ b/grafana-plugin/src/network/grafana-api/http-client.ts @@ -1,6 +1,6 @@ import { getBackendSrv } from '@grafana/runtime'; import { OnCallPluginMetaJSONData } from 'app-types'; -import { getPluginId } from 'helpers/consts'; +import { getPluginId, PluginId } from 'helpers/consts'; import { ApiAuthKeyDTO, @@ -11,18 +11,26 @@ import { UpdateGrafanaPluginSettingsProps, } from './api.types'; +const pluginId = getPluginId(); +const KEY_NAME = { + [PluginId.OnCall]: 'OnCall', + [PluginId.Irm]: 'IRM', +}[pluginId]; +const SERVICE_ACCOUNT_NAME = { + [PluginId.OnCall]: 'sa-autogen-OnCall', + [PluginId.Irm]: 'sa-autogen-IRM', +}[pluginId]; + const KEYS_BASE_URL = '/api/auth/keys'; const SERVICE_ACCOUNTS_BASE_URL = '/api/serviceaccounts'; -const ONCALL_KEY_NAME = 'OnCall'; -const ONCALL_SERVICE_ACCOUNT_NAME = 'sa-autogen-OnCall'; -const GRAFANA_PLUGIN_SETTINGS_URL = `/api/plugins/${getPluginId()}/settings`; +const GRAFANA_PLUGIN_SETTINGS_URL = `/api/plugins/${pluginId}/settings`; export class GrafanaApiClient { static grafanaBackend = getBackendSrv(); private static getServiceAccount = async () => { const serviceAccounts = await this.grafanaBackend.get( - `${SERVICE_ACCOUNTS_BASE_URL}/search?query=${ONCALL_SERVICE_ACCOUNT_NAME}` + `${SERVICE_ACCOUNTS_BASE_URL}/search?query=${SERVICE_ACCOUNT_NAME}` ); return serviceAccounts.serviceAccounts.length > 0 ? serviceAccounts.serviceAccounts[0] : null; }; @@ -34,7 +42,7 @@ export class GrafanaApiClient { } return await this.grafanaBackend.post(SERVICE_ACCOUNTS_BASE_URL, { - name: ONCALL_SERVICE_ACCOUNT_NAME, + name: SERVICE_ACCOUNT_NAME, role: 'Admin', isDisabled: false, }); @@ -44,7 +52,7 @@ export class GrafanaApiClient { const tokens = await this.grafanaBackend.get( `${SERVICE_ACCOUNTS_BASE_URL}/${serviceAccount.id}/tokens` ); - return tokens.find(({ name }) => name === ONCALL_KEY_NAME); + return tokens.find(({ name }) => name === KEY_NAME); }; private static getGrafanaToken = async () => { @@ -54,7 +62,7 @@ export class GrafanaApiClient { } const keys = await this.grafanaBackend.get(KEYS_BASE_URL); - return keys.find(({ name }) => name === ONCALL_KEY_NAME); + return keys.find(({ name }) => name === KEY_NAME); }; static updateGrafanaPluginSettings = async (data: UpdateGrafanaPluginSettingsProps, enabled = true) => @@ -79,7 +87,7 @@ export class GrafanaApiClient { const { key: grafanaToken } = await this.grafanaBackend.post( `${SERVICE_ACCOUNTS_BASE_URL}/${serviceAccount.id}/tokens`, { - name: ONCALL_KEY_NAME, + name: KEY_NAME, role: 'Admin', } );