chore: more adjustments to irm monorepo (#4915)

# What this PR does

- remove hardcoded plugin id so it can run from IRM monorepo context
- minor e2e tests tweaks

## Which issue(s) this PR closes

Related to https://github.com/grafana/irm/issues/41

<!--
*Note*: If you want the issue to be auto-closed once the PR is merged,
change "Related to" to "Closes" in the line above.
If you have more than one GitHub issue that this PR closes, be sure to
preface
each issue link with a [closing
keyword](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests#linking-a-pull-request-to-an-issue).
This ensures that the issue(s) are auto-closed once the PR has been
merged.
-->

## Checklist

- [ ] Unit, integration, and e2e (if applicable) tests updated
- [x] Documentation added (or `pr:no public docs` PR label added if not
required)
- [x] Added the relevant release notes label (see labels prefixed w/
`release:`). These labels dictate how your PR will
    show up in the autogenerated release notes.
This commit is contained in:
Dominik Broj 2024-08-26 07:39:07 +02:00 committed by GitHub
parent a25d44da1a
commit bb7efb655a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 35 additions and 21 deletions

View file

@ -21,4 +21,7 @@ grafana-plugin.yml
# Jest test report
jest_html_reporters.html
jest-html-reporters*
jest-html-reporters*
.turbo
tsconfig.tsbuildinfo

View file

@ -4,18 +4,6 @@ import { goToOnCallPage } from '../utils/navigation';
import { verifyThatUserCanViewOtherUsers, accessProfileTabs } from '../utils/users';
test.describe('Users screen actions', () => {
test("Admin is allowed to edit other users' profile", async ({ adminRolePage: { page } }) => {
await goToOnCallPage(page, 'users');
const editableUsers = page.getByTestId('users-table').getByRole('button', { name: 'Edit', disabled: false });
await editableUsers.first().waitFor();
const editableUsersCount = await editableUsers.count();
expect(editableUsersCount).toBeGreaterThan(1);
});
test('Admin is allowed to view the list of users', async ({ adminRolePage: { page } }) => {
await verifyThatUserCanViewOtherUsers(page);
});
test('Viewer is not allowed to view the list of users', async ({ viewerRolePage: { page } }) => {
await verifyThatUserCanViewOtherUsers(page, false);
});
@ -66,6 +54,18 @@ test.describe('Users screen actions', () => {
expect(usersCountWithDisabledEdit).toBeGreaterThan(1);
});
test("Admin is allowed to edit other users' profile", async ({ adminRolePage: { page } }) => {
await goToOnCallPage(page, 'users');
const editableUsers = page.getByTestId('users-table').getByRole('button', { name: 'Edit', disabled: false });
await editableUsers.first().waitFor();
const editableUsersCount = await editableUsers.count();
expect(editableUsersCount).toBeGreaterThan(1);
});
test('Admin is allowed to view the list of users', async ({ adminRolePage: { page } }) => {
await verifyThatUserCanViewOtherUsers(page);
});
test('Search updates the table view', async ({ adminRolePage }) => {
const { page, userName } = adminRolePage;
await goToOnCallPage(page, 'users');

View file

@ -47,6 +47,7 @@ export const createIntegration = async ({
await searchIntegrationAndAssertItsPresence({ page, integrationName });
await page.getByRole('link', { name: integrationName }).click();
await page.waitForLoadState('networkidle');
};
export const assignEscalationChainToIntegration = async (page: Page, escalationChainName: string): Promise<void> => {

View file

@ -47,7 +47,7 @@ func (a *App) GetPermissions(settings *OnCallPluginSettings, onCallUser *OnCallU
if res.StatusCode == 200 {
var filtered []OnCallPermission
for _, permission := range permissions {
if strings.HasPrefix(permission.Action, "grafana-oncall-app") {
if strings.HasPrefix(permission.Action, settings.PluginID) {
filtered = append(filtered, permission)
}
}
@ -65,7 +65,7 @@ func (a *App) GetAllPermissions(settings *OnCallPluginSettings) (map[string]map[
reqURL.Path += "api/access-control/users/permissions/search"
q := reqURL.Query()
q.Set("actionPrefix", "grafana-oncall-app")
q.Set("actionPrefix", settings.PluginID)
reqURL.RawQuery = q.Encode()
req, err := http.NewRequest("GET", reqURL.String(), nil)

View file

@ -46,6 +46,7 @@ type OnCallPluginSettings struct {
StackID int `json:"stack_id"`
OrgID int `json:"org_id"`
License string `json:"license"`
PluginID string `json:"plugin_id"`
GrafanaURL string `json:"grafana_url"`
GrafanaToken string `json:"grafana_token"`
RBACEnabled bool `json:"rbac_enabled"`
@ -71,6 +72,9 @@ func (a *OnCallPluginSettings) Equal(b *OnCallPluginSettings) bool {
if a.License != b.License {
return false
}
if a.PluginID != b.PluginID {
return false
}
if a.GrafanaURL != b.GrafanaURL {
return false
}
@ -127,6 +131,11 @@ func (a *App) OnCallSettingsFromContext(ctx context.Context) (*OnCallPluginSetti
GrafanaURL: pluginSettingsJson.GrafanaURL,
}
settings.PluginID = pluginContext.PluginID
if settings.PluginID == "" {
return nil, fmt.Errorf("OnCallSettingsFromContext: couldn't get plugin ID from plugin context")
}
version := pluginContext.PluginVersion
if version == "" {
// older Grafana versions do not have the plugin version in the context
@ -286,7 +295,7 @@ func (a *App) SaveOnCallSettings(settings *OnCallPluginSettings) error {
return fmt.Errorf("Marshal OnCall settings JSON: %w", err)
}
settingsUrl, err := url.JoinPath(settings.GrafanaURL, fmt.Sprintf("api/plugins/grafana-oncall-app/settings"))
settingsUrl, err := url.JoinPath(settings.GrafanaURL, fmt.Sprintf("api/plugins/%s/settings", settings.PluginID))
if err != nil {
return err
}

View file

@ -1,6 +1,8 @@
import { getBackendSrv } from '@grafana/runtime';
import { OnCallPluginMetaJSONData } from 'types';
import { getPluginId } from 'utils/consts';
import {
ApiAuthKeyDTO,
NewApiKeyResult,
@ -14,7 +16,7 @@ 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/grafana-oncall-app/settings';
const GRAFANA_PLUGIN_SETTINGS_URL = `/api/plugins/${getPluginId()}/settings`;
export class GrafanaApiClient {
static grafanaBackend = getBackendSrv();

View file

@ -50,7 +50,6 @@ export const BREAKPOINT_TABS = 1024;
// Default redirect page
export const DEFAULT_PAGE = 'alert-groups';
export const PLUGIN_ID = 'grafana-oncall-app';
export const PLUGIN_ROOT = `/a/${getPluginId()}`;
export const PLUGIN_CONFIG = `/plugins/${getPluginId()}`;
@ -82,7 +81,7 @@ const getGrafanaSubUrl = () => {
export const getOnCallApiPath = (subpath = '') => {
// We need to consider the grafanaSubUrl in case Grafana is served from subpath, e.g. http://localhost:3000/grafana
return `${getGrafanaSubUrl()}/api/plugins/${PLUGIN_ID}/resources${subpath}`;
return `${getGrafanaSubUrl()}/api/plugins/${getPluginId()}/resources${subpath}`;
};
// Faro

View file

@ -8,7 +8,7 @@ import { isArray, concat, every, isEmpty, isObject, isPlainObject, flatMap, map,
import { isNetworkError } from 'network/network';
import { CLOUD_VERSION_REGEX, PLUGIN_ID } from './consts';
import { CLOUD_VERSION_REGEX, getPluginId } from './consts';
export class KeyValuePair<T = string | number> {
key: T;
@ -153,7 +153,7 @@ export const isCurrentGrafanaVersionEqualOrGreaterThan = ({
);
};
export const getIsRunningOpenSourceVersion = () => !CLOUD_VERSION_REGEX.test(config.apps[PLUGIN_ID]?.version);
export const getIsRunningOpenSourceVersion = () => !CLOUD_VERSION_REGEX.test(config.apps[getPluginId()]?.version);
export const getIsExternalServiceAccountFeatureAvailable = () =>
isCurrentGrafanaVersionEqualOrGreaterThan({ minMajor: 10, minMinor: 3 }) &&