diff --git a/src/lib/features/export-import-toggles/export-import-service.ts b/src/lib/features/export-import-toggles/export-import-service.ts index 93f3a820adf4..f392b3944b35 100644 --- a/src/lib/features/export-import-toggles/export-import-service.ts +++ b/src/lib/features/export-import-toggles/export-import-service.ts @@ -791,9 +791,14 @@ export default class ExportImportService featureNames = await this.featureTagService.listFeatures(query.tag); } else if (Array.isArray(query.features) && query.features.length) { featureNames = query.features; + } else if (typeof query.project === 'string') { + const allProjectFeatures = await this.toggleStore.getAll({ + project: query.project, + }); + featureNames = allProjectFeatures.map((feature) => feature.name); } else { - const features = await this.toggleStore.getAll(); - featureNames = features.map((feature) => feature.name); + const allFeatures = await this.toggleStore.getAll(); + featureNames = allFeatures.map((feature) => feature.name); } const [ diff --git a/src/lib/features/export-import-toggles/export-import.e2e.test.ts b/src/lib/features/export-import-toggles/export-import.e2e.test.ts index 7ec44f4c11cc..edd9f9604825 100644 --- a/src/lib/features/export-import-toggles/export-import.e2e.test.ts +++ b/src/lib/features/export-import-toggles/export-import.e2e.test.ts @@ -570,6 +570,39 @@ test('returns all features, when no explicit feature was requested', async () => expect(body.features).toHaveLength(2); }); +test('returns all project features', async () => { + await createProjects(); + await createToggle({ + name: defaultFeatureName, + description: 'the #1 feature', + }); + await createToggle({ + name: 'second_feature', + description: 'the #1 feature', + }); + const { body } = await app.request + .post('/api/admin/features-batch/export') + .send({ + environment: 'default', + project: DEFAULT_PROJECT, + }) + .set('Content-Type', 'application/json') + .expect(200); + + expect(body.features).toHaveLength(2); + + const { body: otherProject } = await app.request + .post('/api/admin/features-batch/export') + .send({ + environment: 'default', + project: 'other_project', + }) + .set('Content-Type', 'application/json') + .expect(200); + + expect(otherProject.features).toHaveLength(0); +}); + const variants: VariantsSchema = [ { name: 'variantA', diff --git a/src/lib/openapi/spec/export-query-schema.ts b/src/lib/openapi/spec/export-query-schema.ts index 17aa4652dfd9..5b4d4bca7fc5 100644 --- a/src/lib/openapi/spec/export-query-schema.ts +++ b/src/lib/openapi/spec/export-query-schema.ts @@ -42,8 +42,19 @@ export const exportQuerySchema = { tag: { type: 'string', example: 'release', + description: 'Selects features to export by tag.', + }, + }, + }, + { + required: ['environment', 'project'], + properties: { + ...commonProps, + project: { + type: 'string', + example: 'my-project', description: - 'Selects features to export by tag. Takes precedence over the features field.', + 'Selects project to export the features from. Used when no tags or features are provided.', }, }, },