Skip to content

Commit

Permalink
Minor updates and refactorings
Browse files Browse the repository at this point in the history
  • Loading branch information
matiasb committed Jan 2, 2025
1 parent 83fbe19 commit 9cde2e3
Show file tree
Hide file tree
Showing 19 changed files with 109 additions and 56 deletions.
1 change: 1 addition & 0 deletions dev/.env.dev.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ MATTERMOST_CLIENT_OAUTH_SECRET=
MATTERMOST_HOST=
MATTERMOST_BOT_TOKEN=
MATTERMOST_LOGIN_RETURN_REDIRECT_HOST=http://localhost:8080
MATTERMOST_SIGNING_SECRET=

DJANGO_SETTINGS_MODULE=settings.dev
SECRET_KEY=jyRnfRIeMjYfKdoFa9dKXcNaEGGc8GH1TChmYoWW
Expand Down
3 changes: 0 additions & 3 deletions engine/apps/api/tests/test_organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,3 @@ def test_get_organization_telegram_config_checks(
assert response.status_code == status.HTTP_200_OK
expected_result["is_integration_chatops_connected"] = True
assert response.json() == expected_result


# TODO: Add test to validate mattermost is integrated once integration PR changes are made
1 change: 1 addition & 0 deletions engine/apps/mattermost/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class MattermostBackend(BaseMessagingBackend):
label = "Mattermost"
short_label = "Mattermost"
available_for_use = True
templater = "apps.mattermost.alert_rendering.AlertMattermostTemplater"

def unlink_user(self, user):
from apps.mattermost.models import MattermostUser
Expand Down
18 changes: 17 additions & 1 deletion engine/apps/mattermost/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
import pytest
from django.conf import settings

from apps.mattermost.tests.factories import MattermostMessageFactory, MattermostUserFactory
if not settings.FEATURE_MATTERMOST_INTEGRATION_ENABLED:
pytest.skip("Mattermost integration is not enabled", allow_module_level=True)
else:
from apps.mattermost.tests.factories import (
MattermostChannelFactory,
MattermostMessageFactory,
MattermostUserFactory,
)


@pytest.fixture()
def make_mattermost_channel():
def _make_mattermost_channel(organization, **kwargs):
return MattermostChannelFactory(organization=organization, **kwargs)

return _make_mattermost_channel


@pytest.fixture()
Expand Down
5 changes: 1 addition & 4 deletions engine/common/api_helpers/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
)
from apps.alerts.models import Alert, AlertGroup
from apps.base.messaging import get_messaging_backends
from apps.mattermost.alert_rendering import AlertMattermostTemplater
from common.api_helpers.exceptions import BadRequest
from common.jinja_templater import apply_jinja_template
from common.jinja_templater.apply_jinja_template import JinjaTemplateError, JinjaTemplateWarning
Expand Down Expand Up @@ -239,9 +238,8 @@ def filter_lookups(child):
PHONE_CALL = "phone_call"
SMS = "sms"
TELEGRAM = "telegram"
MATTERMOST = "mattermost"
# templates with its own field in db, this concept replaced by messaging_backend_templates field
NOTIFICATION_CHANNEL_OPTIONS = [SLACK, WEB, PHONE_CALL, SMS, TELEGRAM, MATTERMOST]
NOTIFICATION_CHANNEL_OPTIONS = [SLACK, WEB, PHONE_CALL, SMS, TELEGRAM]

TITLE = "title"
MESSAGE = "message"
Expand All @@ -260,7 +258,6 @@ def filter_lookups(child):
PHONE_CALL: AlertPhoneCallTemplater,
SMS: AlertSmsTemplater,
TELEGRAM: AlertTelegramTemplater,
MATTERMOST: AlertMattermostTemplater,
}

# add additionally supported messaging backends
Expand Down
11 changes: 0 additions & 11 deletions engine/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@
LabelValueFactory,
WebhookAssociatedLabelFactory,
)
from apps.mattermost.tests.factories import MattermostChannelFactory, MattermostMessageFactory
from apps.mobile_app.models import MobileAppAuthToken, MobileAppVerificationToken
from apps.phone_notifications.phone_backend import PhoneBackend
from apps.phone_notifications.tests.factories import PhoneCallRecordFactory, SMSRecordFactory
Expand Down Expand Up @@ -160,8 +159,6 @@
register(AlertReceiveChannelAssociatedLabelFactory)
register(GoogleOAuth2UserFactory)
register(UserNotificationBundleFactory)
register(MattermostChannelFactory)
register(MattermostMessageFactory)

IS_RBAC_ENABLED = os.getenv("ONCALL_TESTING_RBAC_ENABLED", "True") == "True"

Expand Down Expand Up @@ -919,14 +916,6 @@ def _make_telegram_message(alert_group, message_type, **kwargs):
return _make_telegram_message


@pytest.fixture()
def make_mattermost_channel():
def _make_mattermost_channel(organization, **kwargs):
return MattermostChannelFactory(organization=organization, **kwargs)

return _make_mattermost_channel


@pytest.fixture()
def make_phone_call_record():
def _make_phone_call_record(receiver, **kwargs):
Expand Down
6 changes: 4 additions & 2 deletions engine/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,6 @@ class DatabaseTypes:
"drf_spectacular",
"apps.google",
"apps.chatops_proxy",
"apps.mattermost",
]

if DATABASE_TYPE == DatabaseTypes.MYSQL:
Expand Down Expand Up @@ -731,14 +730,17 @@ class BrokerTypes:
SLACK_INTEGRATION_MAINTENANCE_ENABLED = os.environ.get("SLACK_INTEGRATION_MAINTENANCE_ENABLED", False)

# Mattermost
FEATURE_MATTERMOST_INTEGRATION_ENABLED = os.environ.get("FEATURE_MATTERMOST_INTEGRATION_ENABLED", True)
FEATURE_MATTERMOST_INTEGRATION_ENABLED = getenv_boolean("FEATURE_MATTERMOST_INTEGRATION_ENABLED", default=False)
MATTERMOST_CLIENT_OAUTH_ID = os.environ.get("MATTERMOST_CLIENT_OAUTH_ID")
MATTERMOST_CLIENT_OAUTH_SECRET = os.environ.get("MATTERMOST_CLIENT_OAUTH_SECRET")
MATTERMOST_HOST = os.environ.get("MATTERMOST_HOST")
MATTERMOST_BOT_TOKEN = os.environ.get("MATTERMOST_BOT_TOKEN")
MATTERMOST_LOGIN_RETURN_REDIRECT_HOST = os.environ.get("MATTERMOST_LOGIN_RETURN_REDIRECT_HOST", None)
MATTERMOST_SIGNING_SECRET = os.environ.get("MATTERMOST_SIGNING_SECRET", None)

if FEATURE_MATTERMOST_INTEGRATION_ENABLED:
INSTALLED_APPS += ["apps.mattermost"]

SOCIAL_AUTH_SLACK_LOGIN_KEY = SLACK_CLIENT_OAUTH_ID
SOCIAL_AUTH_SLACK_LOGIN_SECRET = SLACK_CLIENT_OAUTH_SECRET
SOCIAL_AUTH_MATTERMOST_LOGIN_KEY = MATTERMOST_CLIENT_OAUTH_ID
Expand Down
10 changes: 6 additions & 4 deletions engine/settings/ci_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@
# request_model = models.Request.objects.create(
SILK_PROFILER_ENABLED = False

# Dummy token
MATTERMOST_HOST = "http://localhost:8065"
MATTERMOST_BOT_TOKEN = "0000000000:XXXXXXXXXXXXXXXXXXXXXXXXXXXX-XXXXXX"
MATTERMOST_SIGNING_SECRET = "f0cb4953bec053e6e616febf2c2392ff60bd02c453a52ab53d9a8b0d0d7284a6"
FEATURE_MATTERMOST_INTEGRATION_ENABLED = True
if FEATURE_MATTERMOST_INTEGRATION_ENABLED:
INSTALLED_APPS += ["apps.mattermost"]
MATTERMOST_HOST = "http://localhost:8065"
MATTERMOST_BOT_TOKEN = "0000000000:XXXXXXXXXXXXXXXXXXXXXXXXXXXX-XXXXXX"
MATTERMOST_SIGNING_SECRET = "f0cb4953bec053e6e616febf2c2392ff60bd02c453a52ab53d9a8b0d0d7284a6"
3 changes: 3 additions & 0 deletions engine/settings/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@
if TESTING:
EXTRA_MESSAGING_BACKENDS = [("apps.base.tests.messaging_backend.TestOnlyBackend", 42)]
TELEGRAM_TOKEN = "0000000000:XXXXXXXXXXXXXXXXXXXXXXXXXXXX-XXXXXX"
MATTERMOST_HOST = "http://localhost:8065"
MATTERMOST_BOT_TOKEN = "0000000000:XXXXXXXXXXXXXXXXXXXXXXXXXXXX-XXXXXX"
MATTERMOST_SIGNING_SECRET = "f0cb4953bec053e6e616febf2c2392ff60bd02c453a52ab53d9a8b0d0d7284a6"

TWILIO_AUTH_TOKEN = "twilio_auth_token"

# charset/collation related tests don't work without this
Expand Down
5 changes: 2 additions & 3 deletions grafana-plugin/src/containers/AlertRules/AlertRules.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@ export const ChatOpsConnectors = (props: ChatOpsConnectorsProps) => {

useEffect(() => {
msteamsChannelStore.updateMSTeamsChannels();
mattermostChannelStore.updateItems();
mattermostChannelStore.updateMattermostChannels();
}, []);

const isMSTeamsInstalled = msteamsChannelStore.currentTeamToMSTeamsChannel?.length > 0;
const connectedChannels = mattermostChannelStore.getSearchResult();
const isMattermostInstalled = store.hasFeature(AppFeature.Mattermost) && connectedChannels && connectedChannels.length
const isMattermostInstalled = store.hasFeature(AppFeature.Mattermost) && mattermostChannelStore.currentTeamToMattermostChannel?.length > 0;

if (!isSlackInstalled && !isTelegramInstalled && !isMSTeamsInstalled && !isMattermostInstalled) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { observer } from 'mobx-react';

import { GSelect } from 'containers/GSelect/GSelect';
import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip';
import { ChannelFilter } from "models/channel_filter/channel_filter.types";
import { ChannelFilter } from 'models/channel_filter/channel_filter.types';
import { MattermostChannel } from 'models/mattermost/mattermost.types';
import { useStore } from 'state/useStore';

Expand All @@ -27,9 +27,9 @@ export const MattermostConnector = observer((props: MattermostConnectorProps) =>
const {
alertReceiveChannelStore,
mattermostChannelStore,

// dereferencing items is needed to rerender GSelect
mattermostChannelStore: { items: mattermostChannelItems },
} = store
} = store;

const channelFilter = alertReceiveChannelStore.channelFilters[channelFilterId];

Expand All @@ -53,9 +53,9 @@ export const MattermostConnector = observer((props: MattermostConnectorProps) =>
<div>
<WithPermissionControlTooltip userAction={UserActions.IntegrationsWrite}>
<InlineSwitch
value={channelFilter.notification_backends?.MATTERMOST?.enabled}
onChange={handleChannelFilterNotifyInMattermostChange}
transparent
value={channelFilter.notification_backends?.MATTERMOST?.enabled}
onChange={handleChannelFilterNotifyInMattermostChange}
transparent
/>
</WithPermissionControlTooltip>
</div>
Expand All @@ -78,4 +78,4 @@ export const MattermostConnector = observer((props: MattermostConnectorProps) =>
</Stack>
</div>
);
})
});
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ export const ExpandedIntegrationRouteDisplay: React.FC<ExpandedIntegrationRouteD
escalationChainStore,
alertReceiveChannelStore,
grafanaTeamStore,
mattermostChannelStore,
} = store;

const channelFilter = alertReceiveChannelStore.channelFilters[channelFilterId];
Expand All @@ -126,7 +125,7 @@ export const ExpandedIntegrationRouteDisplay: React.FC<ExpandedIntegrationRouteD
useEffect(() => {
setIsLoading(true);
(async () => {
await Promise.all([escalationChainStore.updateItems(), telegramChannelStore.updateTelegramChannels(), mattermostChannelStore.updateItems()]);
await Promise.all([escalationChainStore.updateItems(), telegramChannelStore.updateTelegramChannels()]);
setIsLoading(false);
})();
}, []);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ function getGoogleMessage(googleError: GoogleError) {
}

function getMattermostErrorMessage(mattermostError: MattermostError) {
if (mattermostError == MattermostError.MATTERMOST_AUTH_FETCH_USER_ERROR) {
if (mattermostError === MattermostError.MATTERMOST_AUTH_FETCH_USER_ERROR) {
return (
<>
Couldn't connect your Mattermost account. Failed to fetch user information from your mattermost server. Please
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ export const Tabs = ({
[onTabChange]
);

const styles = useStyles2(getUserSettingsPartsStyles);

return (
<TabsBar>
<Tab
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ export const Connectors: FC<ConnectorsProps> = observer((props) => {
<MobileAppConnector {...props} />
<SlackConnector {...props} />
{store.hasFeature(AppFeature.Telegram) && <TelegramConnector {...props} />}
{store.hasFeature(AppFeature.MsTeams) && <MSTeamsConnector {...props} />}
{store.hasFeature(AppFeature.Mattermost) && <MattermostConnector {...props} />}
{store.hasFeature(AppFeature.MsTeams) && <MSTeamsConnector {...props} />}
<Legend>Calendar export</Legend>
<ICalConnector {...props} />
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const MattermostConnector = observer((props: MattermostConnectorProps) =>

const handleConnectButtonClick = useCallback(() => {
onTabChange(UserSettingsTab.MattermostInfo);
}, [onTabChange]);
}, []);

const handleUnlinkMattermostAccount = useCallback(() => {
userStore.unlinkBackend(id, 'MATTERMOST');
Expand All @@ -35,24 +35,28 @@ export const MattermostConnector = observer((props: MattermostConnectorProps) =>

return (
<div>
<InlineField label="Mattermost" labelWidth={12} disabled={!isCurrentUser}>
{mattermostConfigured ? (
{storeUser.messaging_backends.MATTERMOST ? (
<InlineField label="Mattermost" labelWidth={12}>
<Stack gap={StackSize.xs}>
<Input disabled={true} value={mattermostConfigured?.username ? '@' + mattermostConfigured?.username : ''} />
<WithConfirm title="Are you sure to disconnect your mattermost account?" confirmText="Disconnect">
<WithConfirm title="Are you sure to disconnect your Mattermost account?" confirmText="Disconnect">
<Button
onClick={handleUnlinkMattermostAccount}
disabled={!isCurrentUser}
variant="destructive"
icon="times"
disabled={!isCurrentUser}
onClick={handleUnlinkMattermostAccount}
tooltip={'Unlink Mattermost Account'}
/>
</WithConfirm>
</Stack>
) : (
<Button onClick={handleConnectButtonClick}>Connect account</Button>
)}
</InlineField>
</InlineField>
) : (
<div>
<InlineField label="Mattermost" labelWidth={12} disabled={!isCurrentUser}>
<Button onClick={handleConnectButtonClick}>Connect account</Button>
</InlineField>
</div>
)}
</div>
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ export const MattermostInfo = () => {
<Block bordered withBackground className={styles.mattermostInfoblock}>
<Stack direction="column" alignItems="center" gap={StackSize.lg}>
<Text>
Personal Mattermost connection will allow you to manage alert group in your connected mattermost channel
Personal Mattermost connection will allow you to manage alert groups in your connected Mattermost channel
</Text>
<Text>To setup personal mattermost click the button below and login to your mattermost server</Text>
<Text>To link your Mattermost account, click the button below and login to your server</Text>

<Text type="secondary">
More details in{' '}
Expand Down
48 changes: 47 additions & 1 deletion grafana-plugin/src/models/mattermost/mattermost_channel.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { action, observable, makeObservable, runInAction } from 'mobx';
import { action, computed, observable, makeObservable, runInAction } from 'mobx';

import { BaseStore } from 'models/base_store';
import { makeRequest } from 'network/network';
Expand All @@ -10,9 +10,14 @@ export class MattermostChannelStore extends BaseStore {
@observable.shallow
items: { [id: string]: MattermostChannel } = {};

@observable
currentTeamToMattermostChannel?: Array<MattermostChannel['id']>;

@observable.shallow
searchResult: { [key: string]: Array<MattermostChannel['id']> } = {};

private autoUpdateTimer?: ReturnType<typeof setTimeout>;

constructor(rootStore: RootStore) {
super(rootStore);

Expand All @@ -21,6 +26,28 @@ export class MattermostChannelStore extends BaseStore {
this.path = '/mattermost/channels/';
}

@action.bound
async updateMattermostChannels() {
const response = await makeRequest(this.path, {});

const items = response.reduce(
(acc: any, mattermostChannel: MattermostChannel) => ({
...acc,
[mattermostChannel.id]: mattermostChannel,
}),
{}
);

runInAction(() => {
this.items = {
...this.items,
...items,
};

this.currentTeamToMattermostChannel = response.map((mattermostChannel: MattermostChannel) => mattermostChannel.id);
});
}

@action.bound
async updateById(id: MattermostChannel['id']) {
const response = await this.getById(id);
Expand Down Expand Up @@ -65,6 +92,21 @@ export class MattermostChannelStore extends BaseStore {
);
};

@computed
get hasItems() {
return Boolean(this.getSearchResult('')?.length);
}

async startAutoUpdate() {
this.autoUpdateTimer = setInterval(this.updateMattermostChannels.bind(this), 3000);
}

async stopAutoUpdate() {
if (this.autoUpdateTimer) {
clearInterval(this.autoUpdateTimer);
}
}

@action.bound
async makeMattermostChannelDefault(id: MattermostChannel['id']) {
return makeRequest(`/mattermost/channels/${id}/set_default`, {
Expand All @@ -75,4 +117,8 @@ export class MattermostChannelStore extends BaseStore {
async deleteMattermostChannel(id: MattermostChannel['id']) {
return super.delete(id);
}

async getMattermostChannels() {
return super.getAll();
}
}
Loading

0 comments on commit 9cde2e3

Please sign in to comment.