Flagsmith / Flagsmith/flagsmith

Project import endpoint

Open
#7,127 0 comments 0 reactions 1 assignee Claimed by @khvn26 View on GitHub
api
Dominant language
Python
Stars
6.6k
Forks
567
Avg merge
1d 13h
Merged PRs (30d)
121

Description

## Scope

- New model: `ProjectImport`
- Inherits `abstract_base_auditable_model_factory()` for audit log entries.
- `history_record_class_path`, `related_object_type` (new `RelatedObjectType.PROJECT_IMPORT`)
- `project` FK
- `created_by` FK to `FFAdminUser` (nullable)
- `created_by_api_key` FK to `MasterAPIKey` (nullable)
- `strategy` (SKIP / OVERWRITE_DESTRUCTIVE)
- `status` (PROCESSING / SUCCESS / FAILED)
- `data` — `TextField`, 10MB max.
- `result_summary` — `JSONField` (nullable). Populated by the import task on completion:
```python
class ProjectImportResultSummary(TypedDict):
environments_created: int
environments_updated: int
tags_created: int
segments_created: int
segments_deleted: int
features_created: int
features_deleted: int
feature_states_created: int
feature_segments_created: int
identity_overrides_created: int
identity_overrides_deleted: int
```
- `created_at`
- Audit log messages:
- `get_create_log_message`: "Project import requested (strategy: Skip)" or "Project import requested (strategy: Overwrite Destructive)"
- `get_update_log_message`: on SUCCESS, formats `result_summary` into a readable message, e.g. "Project import completed — created 5 features, 3 segments, 1 environment". On FAILED, returns "Project import failed".
- `get_audit_log_author` returns `self.created_by`
- `_get_project` returns `self.project`
- Import serializers — mirror the export serializers but for deserialization:
- Accept name-based references, resolve to DB objects.
- Reuse `FeatureStateValueSerializer` for value deserialization.
- Reuse `SegmentRuleSerializer` / `ConditionSerializer` for segment rule deserialization.
- Entity resolution service, structured as clean functions with clear signatures for future extraction into a shared provisioning core (#6797):
- `resolve_or_create_environment(project, name, settings, strategy) -> Environment`
- SKIP: create if missing, leave existing settings unchanged.
- OVERWRITE_DESTRUCTIVE: create if missing, update existing settings to match.
- `resolve_or_create_tag(project, label, defaults) -> Tag`
- Always create-or-skip (tags are lightweight, no destructive override needed).
- `resolve_or_create_segment(project, name, rules_data, strategy) -> Segment`
- SKIP: create if missing, leave existing unchanged.
- OVERWRITE_DESTRUCTIVE: delete matched segment (cascade deletes rules, conditions, and all FeatureSegment/FeatureState overrides), recreate from data. Reuses `SegmentRuleSerializer` for nested rules.
- `resolve_or_create_feature(project, name, feature_data, strategy) -> Feature`
- SKIP: create if missing, leave existing unchanged.
- OVERWRITE_DESTRUCTIVE: delete matched feature (cascade), recreate from data. If project uses feature versioning, create a new `EnvironmentFeatureVersion` via `versioning_service.update_flag`.
- `create_feature_state(environment, feature, state_data) -> FeatureState`
- Always creates (called after features are resolved, for each environment in the export).
- `create_feature_segment(environment, feature, segment, priority, state_data) -> FeatureSegment`
- Always creates (called after features and segments are resolved).
- New async task: `import_project(project_import_id)`
- Parse JSON, detect format (array = env-level → reject 400, object = project-level).
- For project-level format, call the service functions above in order:
1. Segments
2. Features
3. Environments (+ feature states and feature segments per environment)
4. Tags
- Silently ignores `identity_overrides` in the export data (handled separately).
- Each step runs in its own DB transaction — if step 3 fails, segments and features from steps 1-2 are already committed. The `result_summary` tracks progress so partial imports are visible.
- New views:
- `POST /projects/{project_id}/project-imports/` — upload JSON + strategy.
- `GET /projects/{project_id}/project-imports/` — list imports.
- Request/response serializers for create (file upload + strategy) and list.
- Permissions: organisation admin (destructive operation, matching env-level import precedent).
- Recurring cleanup: extend existing task.
- Migration for new model.

## Acceptance criteria (write as tests first)

- `POST` with file + strategy creates an async import, returns 202.
- SKIP with no conflicts — all entities created.
- SKIP with existing features/segments — existing entities untouched, new ones created.
- OVERWRITE_DESTRUCTIVE with existing features/segments — matched entities deleted and recreated.
- OVERWRITE_DESTRUCTIVE with feature versioning enabled — new versions created for modified features.
- Missing environments created with correct settings from export.
- Existing environment settings updated in OVERWRITE_DESTRUCTIVE, unchanged in SKIP.
- Tags matched by label, created if missing.
- Segments matched by name, rules deserialized via existing serializer.
- Feature states correctly associated with environments.
- Feature segments correctly link features, segments, and environments.
- `identity_overrides` key in export data is silently ignored.
- Invalid/malformed JSON returns 400.
- Env-level format (array) returns 400 with clear error message.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.