# What this PR does Related to https://github.com/grafana/oncall-private/issues/2947 **NOTE** This PR introduces steps 1 and 2 of the 3 part migration proposed [here](https://raintank-corp.slack.com/archives/C06K1MQ07GS/p1732555465144099). Step 3, swapping reads to be from the new-column and dropping dual-writes, will be done in a future PR/release. --- I’m tackling this work now because _ultimately_ I want to move `AlertReceiveChannel.rate_limited_in_slack_at` to `SlackChannel.rate_limited_at` , but first I sorta need to refactor `SlackMessage.channel_id` from a `CHAR` field to a foreign key relationship (because in the spots where we touch Slack rate limiting, like [here](https://github.com/grafana/oncall/blob/dev/engine/apps/slack/alert_group_slack_service.py#L42-L50) for example, we only have `slack_message.channel_id`, which means I need to do extra queries to fetch the appropriate `SlackChannel` to then be able to get/set `SlackChannel.rate_limited_at` Other minor stuffs: - it also prepares us to drop `SlackMessage._slack_team_identity`. We already have a `@property` of `SlackMessage.slack_team_identity` (which [previously had some hacky logic](https://github.com/grafana/oncall/blob/dev/engine/apps/slack/models/slack_message.py#L74-L84)). I've refactored `SlackMessage.slack_team_identity` to simply point to `self.organization.slack_team_identity` + updated our code to _stop_ setting `SlackMessage._slack_team_identity` (will drop this column in future release) ## Checklist - [x] 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.
59 lines
1.8 KiB
Python
59 lines
1.8 KiB
Python
import typing
|
|
|
|
from django.conf import settings
|
|
from django.core.validators import MinLengthValidator
|
|
from django.db import models
|
|
|
|
from common.public_primary_keys import generate_public_primary_key, increase_public_primary_key_length
|
|
|
|
if typing.TYPE_CHECKING:
|
|
from django.db.models.manager import RelatedManager
|
|
|
|
from apps.slack.models import SlackMessage, SlackTeamIdentity
|
|
|
|
|
|
def generate_public_primary_key_for_slack_channel():
|
|
prefix = "H"
|
|
new_public_primary_key = generate_public_primary_key(prefix)
|
|
|
|
failure_counter = 0
|
|
while SlackChannel.objects.filter(public_primary_key=new_public_primary_key).exists():
|
|
new_public_primary_key = increase_public_primary_key_length(
|
|
failure_counter=failure_counter, prefix=prefix, model_name="SlackChannel"
|
|
)
|
|
failure_counter += 1
|
|
|
|
return new_public_primary_key
|
|
|
|
|
|
class SlackChannel(models.Model):
|
|
slack_team_identity: "SlackTeamIdentity"
|
|
slack_messages: "RelatedManager['SlackMessage']"
|
|
|
|
public_primary_key = models.CharField(
|
|
max_length=20,
|
|
validators=[MinLengthValidator(settings.PUBLIC_PRIMARY_KEY_MIN_LENGTH + 1)],
|
|
unique=True,
|
|
default=generate_public_primary_key_for_slack_channel,
|
|
)
|
|
slack_id = models.CharField(max_length=100)
|
|
|
|
slack_team_identity = models.ForeignKey(
|
|
"slack.SlackTeamIdentity",
|
|
on_delete=models.PROTECT,
|
|
related_name="cached_channels",
|
|
null=True,
|
|
default=None,
|
|
)
|
|
name = models.CharField(max_length=500)
|
|
|
|
is_archived = models.BooleanField(default=False)
|
|
is_shared = models.BooleanField(null=True, default=None)
|
|
last_populated = models.DateField(null=True, default=None)
|
|
|
|
class Meta:
|
|
unique_together = ("slack_id", "slack_team_identity")
|
|
|
|
@classmethod
|
|
def __str__(self):
|
|
return f"{self.name}: {self.slack_id}"
|