diff --git a/latest_migrations.manifest b/latest_migrations.manifest index ed1766c1754ab..403e9a98b3f04 100644 --- a/latest_migrations.manifest +++ b/latest_migrations.manifest @@ -5,7 +5,7 @@ contenttypes: 0002_remove_content_type_name ee: 0015_add_verified_properties otp_static: 0002_throttling otp_totp: 0002_auto_20190420_0723 -posthog: 0396_projects_and_environments +posthog: 0397_projects_backfill sessions: 0001_initial social_django: 0010_uid_db_index two_factor: 0007_auto_20201201_1019 diff --git a/plugin-server/functional_tests/api.ts b/plugin-server/functional_tests/api.ts index 539fa266b8dd4..abbd770d7bb77 100644 --- a/plugin-server/functional_tests/api.ts +++ b/plugin-server/functional_tests/api.ts @@ -365,8 +365,18 @@ export const createTeam = async ( token?: string, sessionRecordingOptIn = true ) => { - const team = await insertRow(postgres, 'posthog_team', { + const id = Math.round(Math.random() * 1000000000) + await insertRow(postgres, 'posthog_project', { + // Every team (aka environment) must be a child of a project + id, organization_id: organizationId, + name: 'TEST PROJECT', + created_at: new Date().toISOString(), + }) + await insertRow(postgres, 'posthog_team', { + id, + organization_id: organizationId, + project_id: id, app_urls: [], name: 'TEST PROJECT', event_names: [], @@ -392,7 +402,7 @@ export const createTeam = async ( access_control: false, slack_incoming_webhook, }) - return team.id + return id } export const createAction = async (action: Omit, steps: Omit[]) => { diff --git a/plugin-server/tests/helpers/sql.ts b/plugin-server/tests/helpers/sql.ts index eb167cccdb20f..8564250118550 100644 --- a/plugin-server/tests/helpers/sql.ts +++ b/plugin-server/tests/helpers/sql.ts @@ -224,8 +224,15 @@ export async function createUserTeamAndOrganization( joined_at: new Date().toISOString(), updated_at: new Date().toISOString(), }) + await insertRow(db, 'posthog_project', { + id: teamId, + organization_id: organizationId, + name: 'TEST PROJECT', + created_at: new Date().toISOString(), + }) await insertRow(db, 'posthog_team', { id: teamId, + project_id: teamId, organization_id: organizationId, app_urls: [], name: 'TEST PROJECT', @@ -315,10 +322,19 @@ export const createOrganization = async (pg: PostgresRouter) => { } export const createTeam = async (pg: PostgresRouter, organizationId: string, token?: string) => { - const team = await insertRow(pg, 'posthog_team', { - // KLUDGE: auto increment IDs can be racy in tests so we ensure IDs don't clash - id: Math.round(Math.random() * 1000000000), + // KLUDGE: auto increment IDs can be racy in tests so we ensure IDs don't clash + const id = Math.round(Math.random() * 1000000000) + await insertRow(pg, 'posthog_project', { + // Every team (aka environment) must be a child of a project + id, + organization_id: organizationId, + name: 'TEST PROJECT', + created_at: new Date().toISOString(), + }) + await insertRow(pg, 'posthog_team', { + id, organization_id: organizationId, + project_id: id, app_urls: [], name: 'TEST PROJECT', event_names: [], @@ -343,7 +359,7 @@ export const createTeam = async (pg: PostgresRouter, organizationId: string, tok person_display_name_properties: [], access_control: false, }) - return team.id + return id } export const createUser = async (pg: PostgresRouter, distinctId: string) => { diff --git a/posthog/migrations/0397_projects_backfill.py b/posthog/migrations/0397_projects_backfill.py new file mode 100644 index 0000000000000..5b3da405af116 --- /dev/null +++ b/posthog/migrations/0397_projects_backfill.py @@ -0,0 +1,41 @@ +# Generated by Django 4.1.13 on 2024-03-12 23:14 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("posthog", "0396_projects_and_environments"), + ] + + operations = [ + migrations.SeparateDatabaseAndState( + database_operations=[ + migrations.RunSQL( + sql=""" + -- For each team without a parent project, create such a project + INSERT INTO posthog_project (id, name, created_at, organization_id) + SELECT id, name, created_at, organization_id + FROM posthog_team + WHERE project_id IS NULL; + -- At this point, all teams have a parent project, so we can safely set project_id on every team + UPDATE posthog_team + SET project_id = id;""", + reverse_sql=migrations.RunSQL.noop, + ) + ], + state_operations=[ + migrations.AlterField( + model_name="team", + name="project", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="teams", + related_query_name="team", + to="posthog.project", + ), + ), + ], + ) + ] diff --git a/posthog/models/team/team.py b/posthog/models/team/team.py index 93a486cdc98fc..66b4a3ed51415 100644 --- a/posthog/models/team/team.py +++ b/posthog/models/team/team.py @@ -175,8 +175,6 @@ class Team(UUIDClassicModel): on_delete=models.CASCADE, related_name="teams", related_query_name="team", - null=True, - blank=False, ) api_token: models.CharField = models.CharField( max_length=200,