commit
50dbfd1e5a
9 changed files with 415 additions and 145 deletions
|
|
@ -18,7 +18,7 @@ charset-normalizer==3.3.2
|
|||
# requests
|
||||
distlib==0.3.8
|
||||
# via virtualenv
|
||||
django==4.2.18
|
||||
django==4.2.19
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# django-stubs
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ babel==2.12.1
|
|||
beautifulsoup4==4.12.2
|
||||
celery[redis]==5.3.6
|
||||
cryptography==43.0.1
|
||||
django==4.2.18
|
||||
django==4.2.19
|
||||
django-add-default-value==0.10.0
|
||||
django-anymail[amazon-ses]==12.0
|
||||
django-cors-headers==3.7.0
|
||||
|
|
@ -14,7 +14,7 @@ django-ipware==4.0.2
|
|||
django-log-request-id==1.6.0
|
||||
django-migration-linter==4.1.0
|
||||
django-mirage-field==1.3.0
|
||||
django-mysql==4.6.0
|
||||
django-mysql==4.16.0
|
||||
django-polymorphic==3.1.0
|
||||
django-ratelimit==2.0.0
|
||||
django-redis==5.4.0
|
||||
|
|
@ -23,7 +23,7 @@ django-silk==5.0.3
|
|||
django-sns-view==0.1.2
|
||||
djangorestframework==3.15.2
|
||||
factory-boy<3.0
|
||||
drf-spectacular==0.26.5
|
||||
drf-spectacular==0.28.0
|
||||
emoji==2.4.0
|
||||
# If the version of grpcio is changed
|
||||
# upload a new arm64 wheel instead of /engine/grpcio-1.57.0-cp311-cp311-linux_aarch64.whl
|
||||
|
|
@ -32,7 +32,7 @@ fcm-django @ https://github.com/grafana/fcm-django/archive/refs/tags/v1.0.12r1.t
|
|||
hiredis==2.2.3
|
||||
humanize==4.10.0
|
||||
icalendar==5.0.10
|
||||
jinja2==3.1.5
|
||||
jinja2==3.1.6
|
||||
lxml==5.2.2
|
||||
markdown2==2.4.10
|
||||
opentelemetry-sdk==1.26.0
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ deprecated==1.2.14
|
|||
# opentelemetry-api
|
||||
# opentelemetry-exporter-otlp-proto-grpc
|
||||
# opentelemetry-semantic-conventions
|
||||
django==4.2.18
|
||||
django==4.2.19
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-add-default-value
|
||||
|
|
@ -115,7 +115,7 @@ django-migration-linter==4.1.0
|
|||
# via -r requirements.in
|
||||
django-mirage-field==1.3.0
|
||||
# via -r requirements.in
|
||||
django-mysql==4.6.0
|
||||
django-mysql==4.16.0
|
||||
# via -r requirements.in
|
||||
django-polymorphic==3.1.0
|
||||
# via
|
||||
|
|
@ -136,7 +136,7 @@ djangorestframework==3.15.2
|
|||
# -r requirements.in
|
||||
# django-rest-polymorphic
|
||||
# drf-spectacular
|
||||
drf-spectacular==0.26.5
|
||||
drf-spectacular==0.28.0
|
||||
# via -r requirements.in
|
||||
emoji==2.4.0
|
||||
# via
|
||||
|
|
@ -229,7 +229,7 @@ inflection==0.5.1
|
|||
# via drf-spectacular
|
||||
itsdangerous==2.1.2
|
||||
# via flask
|
||||
jinja2==3.1.5
|
||||
jinja2==3.1.6
|
||||
# via
|
||||
# -r requirements.in
|
||||
# flask
|
||||
|
|
|
|||
77
grafana-plugin/e2e-tests/users/personalWebhook.test.ts
Normal file
77
grafana-plugin/e2e-tests/users/personalWebhook.test.ts
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
import { test, expect } from '../fixtures';
|
||||
import grafanaApiClient from '../utils/clients/grafana';
|
||||
import { clickButton, generateRandomValue } from '../utils/forms';
|
||||
import { goToOnCallPage } from '../utils/navigation';
|
||||
import { checkWebhookPresenceInTable } from '../utils/outgoingWebhooks';
|
||||
|
||||
const WEBHOOK_NAME = generateRandomValue();
|
||||
const TRIGGER_TYPE = 'Personal Notification';
|
||||
|
||||
let webhookID: string;
|
||||
|
||||
test.afterAll(async ({ request }) => {
|
||||
// Delete the created webhook
|
||||
if (webhookID) {
|
||||
await grafanaApiClient.makeRequest(
|
||||
request,
|
||||
`resources/webhooks/${webhookID}/`,
|
||||
'delete',
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
test('Connects a personal notification webhook', async ({ adminRolePage: { page } }) => {
|
||||
// Create a new webhook
|
||||
await goToOnCallPage(page, 'outgoing_webhooks');
|
||||
await page.getByRole('button', { name: 'New Outgoing Webhook' }).click();
|
||||
|
||||
// Choose Advanced webhook
|
||||
await page.getByTestId('create-outgoing-webhook-modal').locator('div').filter({ hasText: 'AdvancedAn advanced webhook' }).first().click();
|
||||
|
||||
// Give it a name
|
||||
await page.locator('input[name="name"]').fill(WEBHOOK_NAME);
|
||||
|
||||
// Choose a trigger type
|
||||
await page.getByTestId('triggerType-selector').locator('div').nth(1).click();
|
||||
await page.getByLabel('Select options menu').getByText(TRIGGER_TYPE).click();
|
||||
|
||||
// Set a URL
|
||||
await page.locator('#OutgoingWebhook div').locator('.monaco-editor').first().click();
|
||||
await page.keyboard.insertText('https://example.com');
|
||||
|
||||
// Create and check it has been created
|
||||
const responsePromise = page.waitForResponse('**/resources/webhooks/');
|
||||
await clickButton({ page, buttonText: 'Create' });
|
||||
await checkWebhookPresenceInTable({ page, webhookName: WEBHOOK_NAME, expectedTriggerType: TRIGGER_TYPE });
|
||||
|
||||
// save the ID so we can delete the webhook after the tests have run
|
||||
const response = await responsePromise;
|
||||
const wh = await response.json();
|
||||
webhookID = wh?.id;
|
||||
|
||||
await goToOnCallPage(page, 'users/me');
|
||||
await page.getByRole('tab', { name: 'Webhook connection' }).click();
|
||||
|
||||
// Select webhook
|
||||
await page.getByRole('dialog').locator('svg').nth(2).click();
|
||||
await page.getByLabel('Select options menu').getByText(WEBHOOK_NAME).click();
|
||||
|
||||
// Add some context
|
||||
await page.getByRole('textbox').fill('{ "test": true }');
|
||||
|
||||
// Connect
|
||||
await page.getByRole('button', { name: 'Connect' }).click();
|
||||
|
||||
// Check connection on User Info tab
|
||||
await page.getByRole('tab', { name: 'User info' }).click();
|
||||
expect(page.getByText(WEBHOOK_NAME)).toBeVisible();
|
||||
|
||||
// Disconnect
|
||||
await page.getByRole('tab', { name: 'Webhook connection' }).click();
|
||||
await page.getByRole('button', { name: 'Disconnect' }).click();
|
||||
await page.getByTestId('data-testid Confirm Modal Danger Button').click();
|
||||
|
||||
// Check connection is no longer shown
|
||||
await page.getByRole('tab', { name: 'User info' }).click();
|
||||
expect(page.getByText(WEBHOOK_NAME)).not.toBeVisible();
|
||||
})
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { OrgRole } from '@grafana/data';
|
||||
import { expect, APIRequestContext } from '@playwright/test';
|
||||
import { expect, APIRequestContext, APIResponse } from '@playwright/test';
|
||||
|
||||
import { BASE_URL, GRAFANA_ADMIN_PASSWORD, GRAFANA_ADMIN_USERNAME } from '../constants';
|
||||
|
||||
|
|
@ -128,6 +128,14 @@ class GrafanaAPIClient {
|
|||
const data: GetSettingsResponse = await res.json();
|
||||
return data.buildInfo.version;
|
||||
};
|
||||
|
||||
makeRequest = async (request: APIRequestContext, path: string, method: 'get' | 'post' | 'put' | 'delete' = 'post'): Promise<APIResponse> => {
|
||||
const res = await request[method](`${BASE_URL}/api/plugins/grafana-oncall-app/${path.replace(/^\//, '')}`, {
|
||||
headers: this.requestHeaders,
|
||||
});
|
||||
expect(res.ok()).toBeTruthy();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
const grafanaAPIClient = new GrafanaAPIClient(GRAFANA_ADMIN_USERNAME, GRAFANA_ADMIN_PASSWORD);
|
||||
|
|
|
|||
|
|
@ -78,10 +78,10 @@ require (
|
|||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.29.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/text v0.18.0 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd // indirect
|
||||
|
|
|
|||
|
|
@ -235,13 +235,13 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191020152052-9984515f0562/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
@ -255,12 +255,12 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@
|
|||
"@typescript-eslint/eslint-plugin": "^5.40.1",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"css-loader": "^6.7.3",
|
||||
"dompurify": "^2.3.12",
|
||||
"dompurify": "^3.2.4",
|
||||
"eslint": "^8.25.0",
|
||||
"eslint-plugin-deprecation": "^2.0.0",
|
||||
"eslint-plugin-jsdoc": "^44.2.4",
|
||||
|
|
@ -95,7 +95,7 @@
|
|||
"eslint-plugin-rulesdir": "^0.2.1",
|
||||
"eslint-plugin-unused-imports": "^3.1.0",
|
||||
"eslint-webpack-plugin": "^4.0.1",
|
||||
"express": "^4.19.2",
|
||||
"express": "^4.20.0",
|
||||
"fork-ts-checker-webpack-plugin": "^8.0.0",
|
||||
"glob": "^10.2.7",
|
||||
"identity-obj-proxy": "3.0.0",
|
||||
|
|
|
|||
427
grafana-plugin/pnpm-lock.yaml
generated
427
grafana-plugin/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue