diff --git a/mockserver/tenants/migrations/0012_organization_feature_flags.py b/mockserver/tenants/migrations/0012_organization_feature_flags.py new file mode 100644 index 0000000..871da8b --- /dev/null +++ b/mockserver/tenants/migrations/0012_organization_feature_flags.py @@ -0,0 +1,30 @@ +# Generated by Django 3.0.9 on 2021-01-31 00:19 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('tenants', '0011_project_slug_name'), + ] + + operations = [ + migrations.CreateModel( + name='FeatureFlag', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(auto_now_add=True)), + ('last_modified', models.DateTimeField(auto_now=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddField( + model_name='organization', + name='feature_flags', + field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='organization', to='tenants.FeatureFlag'), + ), + ] diff --git a/mockserver/tenants/migrations/0013_organization_profile_fix.py b/mockserver/tenants/migrations/0013_organization_profile_fix.py new file mode 100644 index 0000000..2adcd31 --- /dev/null +++ b/mockserver/tenants/migrations/0013_organization_profile_fix.py @@ -0,0 +1,34 @@ +# Generated by Django 3.0.9 on 2021-01-31 01:50 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('tenants', '0012_organization_feature_flags'), + ] + + operations = [ + migrations.AddField( + model_name='featureflag', + name='organization', + field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='featureflag', to='tenants.Organization'), + ), + migrations.AddField( + model_name='organizationprofile', + name='organization', + field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='public_profile', to='tenants.Organization'), + ), + migrations.AlterField( + model_name='organization', + name='feature_flags', + field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='deprecated_org', to='tenants.FeatureFlag'), + ), + migrations.AlterField( + model_name='organization', + name='profile', + field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='deprecated_org', to='tenants.OrganizationProfile'), + ), + ] diff --git a/mockserver/tenants/migrations/0014_organization_data_migration.py b/mockserver/tenants/migrations/0014_organization_data_migration.py new file mode 100644 index 0000000..16799aa --- /dev/null +++ b/mockserver/tenants/migrations/0014_organization_data_migration.py @@ -0,0 +1,31 @@ +# Generated by Django 3.0.9 on 2021-01-31 04:36 + +from django.db import migrations + + +def forwards(apps, schema_editor): + Organization = apps.get_model('tenants', 'Organization') + + for org in Organization.objects.all(): + if org.profile is not None: + org.profile.organization = org + org.profile.save() + + if org.feature_flags is not None: + org.feature_flags.organization = org + org.feature_flags.save() + + +def backwards(apps, schema_editor): + ... + + +class Migration(migrations.Migration): + + dependencies = [ + ('tenants', '0013_organization_profile_fix'), + ] + + operations = [ + migrations.RunPython(forwards, backwards) + ] diff --git a/mockserver/tenants/migrations/0015_organization_fields_remove.py b/mockserver/tenants/migrations/0015_organization_fields_remove.py new file mode 100644 index 0000000..7daaa47 --- /dev/null +++ b/mockserver/tenants/migrations/0015_organization_fields_remove.py @@ -0,0 +1,21 @@ +# Generated by Django 3.0.9 on 2021-01-31 04:55 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('tenants', '0014_organization_data_migration'), + ] + + operations = [ + migrations.RemoveField( + model_name='organization', + name='feature_flags', + ), + migrations.RemoveField( + model_name='organization', + name='profile', + ), + ] diff --git a/mockserver/tenants/migrations/0016_organization_rename_reverse.py b/mockserver/tenants/migrations/0016_organization_rename_reverse.py new file mode 100644 index 0000000..41266b9 --- /dev/null +++ b/mockserver/tenants/migrations/0016_organization_rename_reverse.py @@ -0,0 +1,24 @@ +# Generated by Django 3.0.9 on 2021-01-31 05:00 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('tenants', '0015_organization_fields_remove'), + ] + + operations = [ + migrations.AlterField( + model_name='featureflag', + name='organization', + field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='feature_flags', to='tenants.Organization'), + ), + migrations.AlterField( + model_name='organizationprofile', + name='organization', + field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='profile', to='tenants.Organization'), + ), + ] diff --git a/mockserver/tenants/models.py b/mockserver/tenants/models.py index 1009284..dfa47d1 100644 --- a/mockserver/tenants/models.py +++ b/mockserver/tenants/models.py @@ -62,6 +62,13 @@ class Meta: class OrganizationProfile(models.Model): + organization = models.OneToOneField( + 'tenants.Organization', + related_name="profile", + on_delete=models.CASCADE, + null=True + ) + public_name = models.CharField(max_length=255) description = models.TextField(null=True) technologies = models.ManyToManyField( @@ -94,20 +101,19 @@ class Organization(DateAwareModel): through=OrganizationMembership, blank=True ) - profile = models.OneToOneField( - 'tenants.OrganizationProfile', - related_name='organization', - on_delete=models.CASCADE, - null=True - ) def save(self, **kwargs): - if not self.pk: - self.profile = OrganizationProfile.objects.create( + is_new = not self.pk + super(Organization, self).save(**kwargs) + + if is_new: + OrganizationProfile.objects.create( organization=self, public_name=self.name ) - return super(Organization, self).save(**kwargs) + FeatureFlag.objects.create( + organization=self, + ) @property def member_count(self): @@ -121,6 +127,15 @@ def __str__(self): return f"{self.name} ({self.uuid})" +class FeatureFlag(DateAwareModel): + organization = models.OneToOneField( + 'tenants.Organization', + related_name="feature_flags", + on_delete=models.CASCADE, + null=True + ) + + class Tenant(DateAwareModel, User): pass diff --git a/mockserver/tenants/tests/test_models.py b/mockserver/tenants/tests/test_models.py index a52e8f8..ba6525d 100644 --- a/mockserver/tenants/tests/test_models.py +++ b/mockserver/tenants/tests/test_models.py @@ -5,18 +5,36 @@ from common.tests.mixins import MockTestMixin from tenants.models import ( OrganizationInvite, - OrganizationProfile + OrganizationProfile, + FeatureFlag ) class OrganizationModelTestCase(MockTestMixin, TestCase): - def test_new_organization_creates_profile(self): + def test_organization_handles_profile(self): organization = self.create_bare_minimum_organization() - self.assertIsNotNone(organization.profile) - self.assertEqual(OrganizationProfile.objects.count(), 1) - self.assertEqual(organization.profile.public_name, organization.name) - self.assertEqual(organization.profile.organization.pk, organization.pk) + with self.subTest("automatically creates profile"): + self.assertIsNotNone(organization.profile) + self.assertEqual(OrganizationProfile.objects.count(), 1) + self.assertEqual(organization.profile.public_name, organization.name) + self.assertEqual(organization.profile.organization.pk, organization.pk) + with self.subTest("automatically deletes profile"): + organization.delete() + + self.assertEqual(OrganizationProfile.objects.count(), 0) + + + def test_organization_handles_feature_flags(self): + organization = self.create_bare_minimum_organization() + + with self.subTest("automatically creates feature flag"): + self.assertIsNotNone(organization.feature_flags) + self.assertEqual(FeatureFlag.objects.count(), 1) + with self.subTest("automatically deletes feature flag"): + organization.delete() + + self.assertEqual(FeatureFlag.objects.count(), 0) class OrganizationInviteModelTestCase(MockTestMixin, TestCase):