mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-04 01:18:20 +02:00
fix: feature not found in project yields 404 (#3958)
## About the changes When a feature is not found in a project we should fail with a NotFound error. If the feature belongs to a different project, it should not be a permission issue, because the user might not be aware (lack of permissions/visibility) of that other project, so even in this case the error should be NotFound (this also works if we ever allow the same feature name in different projects) Fixes #3726 --------- Co-authored-by: Thomas Heartman <thomas@getunleash.ai>
This commit is contained in:
parent
7003351b35
commit
818b8e7813
@ -206,8 +206,12 @@ class FeatureToggleService {
|
|||||||
const id = await this.featureToggleStore.getProjectId(featureName);
|
const id = await this.featureToggleStore.getProjectId(featureName);
|
||||||
|
|
||||||
if (id !== projectId) {
|
if (id !== projectId) {
|
||||||
throw new InvalidOperationError(
|
throw new NotFoundError(
|
||||||
`The operation could not be completed. The feature exists, but the provided project id ("${projectId}") does not match the project that the feature belongs to ("${id}"). Try using "${id}" in the request URL instead of "${projectId}".`,
|
`There's no feature named "${featureName}" in project "${projectId}"${
|
||||||
|
id === undefined
|
||||||
|
? '.'
|
||||||
|
: `, but there's a feature with that name in project "${id}"`
|
||||||
|
}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ export interface IFeatureToggleQuery {
|
|||||||
export interface IFeatureToggleStore extends Store<FeatureToggle, string> {
|
export interface IFeatureToggleStore extends Store<FeatureToggle, string> {
|
||||||
count(query?: Partial<IFeatureToggleQuery>): Promise<number>;
|
count(query?: Partial<IFeatureToggleQuery>): Promise<number>;
|
||||||
setLastSeen(toggleNames: string[]): Promise<void>;
|
setLastSeen(toggleNames: string[]): Promise<void>;
|
||||||
getProjectId(name: string): Promise<string>;
|
getProjectId(name: string): Promise<string | undefined>;
|
||||||
create(project: string, data: FeatureToggleDTO): Promise<FeatureToggle>;
|
create(project: string, data: FeatureToggleDTO): Promise<FeatureToggle>;
|
||||||
update(project: string, data: FeatureToggleDTO): Promise<FeatureToggle>;
|
update(project: string, data: FeatureToggleDTO): Promise<FeatureToggle>;
|
||||||
archive(featureName: string): Promise<FeatureToggle>;
|
archive(featureName: string): Promise<FeatureToggle>;
|
||||||
|
@ -575,7 +575,7 @@ test('Trying to toggle environment that does not exist yields 404', async () =>
|
|||||||
test('Getting feature that does not exist should yield 404', async () => {
|
test('Getting feature that does not exist should yield 404', async () => {
|
||||||
await app.request
|
await app.request
|
||||||
.get('/api/admin/projects/default/features/non.existing.feature')
|
.get('/api/admin/projects/default/features/non.existing.feature')
|
||||||
.expect(403);
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Interacting with features using project IDs that belong to other projects', () => {
|
describe('Interacting with features using project IDs that belong to other projects', () => {
|
||||||
@ -617,34 +617,34 @@ describe('Interacting with features using project IDs that belong to other proje
|
|||||||
await db.stores.userStore.deleteAll();
|
await db.stores.userStore.deleteAll();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Getting a feature yields 403 if the provided project id doesn't match the feature's project", async () => {
|
test("Getting a feature yields 404 if the provided project id doesn't match the feature's project", async () => {
|
||||||
await app.request
|
await app.request
|
||||||
.get(`/api/admin/projects/${otherProject}/features/${featureName}`)
|
.get(`/api/admin/projects/${otherProject}/features/${featureName}`)
|
||||||
.expect(403);
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Getting a feature yields 403 if the provided project doesn't exist", async () => {
|
test("Getting a feature yields 404 if the provided project doesn't exist", async () => {
|
||||||
await app.request
|
await app.request
|
||||||
.get(
|
.get(
|
||||||
`/api/admin/projects/${nonExistingProject}/features/${featureName}`,
|
`/api/admin/projects/${nonExistingProject}/features/${featureName}`,
|
||||||
)
|
)
|
||||||
.expect(403);
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Archiving a feature yields 403 if the provided project id doesn't match the feature's project", async () => {
|
test("Archiving a feature yields 404 if the provided project id doesn't match the feature's project", async () => {
|
||||||
await app.request
|
await app.request
|
||||||
.delete(
|
.delete(
|
||||||
`/api/admin/projects/${otherProject}/features/${featureName}`,
|
`/api/admin/projects/${otherProject}/features/${featureName}`,
|
||||||
)
|
)
|
||||||
.expect(403);
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Archiving a feature yields 403 if the provided project doesn't exist", async () => {
|
test("Archiving a feature yields 404 if the provided project doesn't exist", async () => {
|
||||||
await app.request
|
await app.request
|
||||||
.delete(
|
.delete(
|
||||||
`/api/admin/projects/${nonExistingProject}/features/${featureName}`,
|
`/api/admin/projects/${nonExistingProject}/features/${featureName}`,
|
||||||
)
|
)
|
||||||
.expect(403);
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Trying to archive a feature that doesn't exist should yield a 404, regardless of whether the project exists or not.", async () => {
|
test("Trying to archive a feature that doesn't exist should yield a 404, regardless of whether the project exists or not.", async () => {
|
||||||
@ -1055,7 +1055,7 @@ test('add strategy cannot use wrong projectId', async () => {
|
|||||||
userIds: '',
|
userIds: '',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.expect(403);
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('update strategy on feature toggle cannot use wrong projectId', async () => {
|
test('update strategy on feature toggle cannot use wrong projectId', async () => {
|
||||||
@ -2215,9 +2215,9 @@ test('should validate context when calling update with PUT', async () => {
|
|||||||
.put(`/api/admin/projects/another-project/features/${name}`)
|
.put(`/api/admin/projects/another-project/features/${name}`)
|
||||||
.send({ name, description: 'updated', type: 'kill-switch' })
|
.send({ name, description: 'updated', type: 'kill-switch' })
|
||||||
.expect((res) => {
|
.expect((res) => {
|
||||||
expect(res.body.name).toBe('InvalidOperationError');
|
expect(res.body.name).toBe('NotFoundError');
|
||||||
})
|
})
|
||||||
.expect(403);
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should validate context when calling update with PATCH', async () => {
|
test('should validate context when calling update with PATCH', async () => {
|
||||||
@ -2231,9 +2231,9 @@ test('should validate context when calling update with PATCH', async () => {
|
|||||||
.patch(`/api/admin/projects/another-project/features/${name}`)
|
.patch(`/api/admin/projects/another-project/features/${name}`)
|
||||||
.send([])
|
.send([])
|
||||||
.expect((res) => {
|
.expect((res) => {
|
||||||
expect(res.body.name).toBe('InvalidOperationError');
|
expect(res.body.name).toBe('NotFoundError');
|
||||||
})
|
})
|
||||||
.expect(403);
|
.expect(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not update project with PUT', async () => {
|
test('should not update project with PUT', async () => {
|
||||||
|
Loading…
Reference in New Issue
Block a user