mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-23 01:16:27 +02:00
fix: disable projects (#1085)
This commit is contained in:
parent
ce16c5a77b
commit
ec60f4485c
23
src/lib/error/minimum-one-environment-error.ts
Normal file
23
src/lib/error/minimum-one-environment-error.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
class MinimumOneEnvironmentError extends Error {
|
||||||
|
constructor(message: string) {
|
||||||
|
super();
|
||||||
|
Error.captureStackTrace(this, this.constructor);
|
||||||
|
|
||||||
|
this.name = this.constructor.name;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON(): object {
|
||||||
|
return {
|
||||||
|
isJoi: true,
|
||||||
|
name: this.constructor.name,
|
||||||
|
details: [
|
||||||
|
{
|
||||||
|
message: this.message,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default MinimumOneEnvironmentError;
|
||||||
|
module.exports = MinimumOneEnvironmentError;
|
@ -54,6 +54,8 @@ export const handleErrors: (
|
|||||||
return res.status(400).json(error).end();
|
return res.status(400).json(error).end();
|
||||||
case 'IncompatibleProjectError':
|
case 'IncompatibleProjectError':
|
||||||
return res.status(403).json(error).end();
|
return res.status(403).json(error).end();
|
||||||
|
case 'MinimumOneEnvironmentError':
|
||||||
|
return res.status(400).json(error).end();
|
||||||
default:
|
default:
|
||||||
logger.error('Server failed executing request', error);
|
logger.error('Server failed executing request', error);
|
||||||
return res.status(500).end();
|
return res.status(500).end();
|
||||||
|
@ -9,6 +9,8 @@ import NotFoundError from '../error/notfound-error';
|
|||||||
import { IEnvironmentStore } from '../types/stores/environment-store';
|
import { IEnvironmentStore } from '../types/stores/environment-store';
|
||||||
import { IFeatureStrategiesStore } from '../types/stores/feature-strategies-store';
|
import { IFeatureStrategiesStore } from '../types/stores/feature-strategies-store';
|
||||||
import { IFeatureEnvironmentStore } from '../types/stores/feature-environment-store';
|
import { IFeatureEnvironmentStore } from '../types/stores/feature-environment-store';
|
||||||
|
import { IProjectStore } from 'lib/types/stores/project-store';
|
||||||
|
import MinimumOneEnvironmentError from '../error/minimum-one-environment-error';
|
||||||
|
|
||||||
export default class EnvironmentService {
|
export default class EnvironmentService {
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
@ -17,6 +19,8 @@ export default class EnvironmentService {
|
|||||||
|
|
||||||
private featureStrategiesStore: IFeatureStrategiesStore;
|
private featureStrategiesStore: IFeatureStrategiesStore;
|
||||||
|
|
||||||
|
private projectStore: IProjectStore;
|
||||||
|
|
||||||
private featureEnvironmentStore: IFeatureEnvironmentStore;
|
private featureEnvironmentStore: IFeatureEnvironmentStore;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -24,11 +28,13 @@ export default class EnvironmentService {
|
|||||||
environmentStore,
|
environmentStore,
|
||||||
featureStrategiesStore,
|
featureStrategiesStore,
|
||||||
featureEnvironmentStore,
|
featureEnvironmentStore,
|
||||||
|
projectStore,
|
||||||
}: Pick<
|
}: Pick<
|
||||||
IUnleashStores,
|
IUnleashStores,
|
||||||
| 'environmentStore'
|
| 'environmentStore'
|
||||||
| 'featureStrategiesStore'
|
| 'featureStrategiesStore'
|
||||||
| 'featureEnvironmentStore'
|
| 'featureEnvironmentStore'
|
||||||
|
| 'projectStore'
|
||||||
>,
|
>,
|
||||||
{ getLogger }: Pick<IUnleashConfig, 'getLogger'>,
|
{ getLogger }: Pick<IUnleashConfig, 'getLogger'>,
|
||||||
) {
|
) {
|
||||||
@ -36,6 +42,7 @@ export default class EnvironmentService {
|
|||||||
this.environmentStore = environmentStore;
|
this.environmentStore = environmentStore;
|
||||||
this.featureStrategiesStore = featureStrategiesStore;
|
this.featureStrategiesStore = featureStrategiesStore;
|
||||||
this.featureEnvironmentStore = featureEnvironmentStore;
|
this.featureEnvironmentStore = featureEnvironmentStore;
|
||||||
|
this.projectStore = projectStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAll(): Promise<IEnvironment[]> {
|
async getAll(): Promise<IEnvironment[]> {
|
||||||
@ -91,13 +98,23 @@ export default class EnvironmentService {
|
|||||||
environment: string,
|
environment: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.featureEnvironmentStore.disconnectFeatures(
|
const projectEnvs = await this.projectStore.getEnvironmentsForProject(
|
||||||
environment,
|
|
||||||
projectId,
|
projectId,
|
||||||
);
|
);
|
||||||
await this.featureEnvironmentStore.disconnectProject(
|
|
||||||
environment,
|
if (projectEnvs.length > 1) {
|
||||||
projectId,
|
await this.featureEnvironmentStore.disconnectFeatures(
|
||||||
|
environment,
|
||||||
|
projectId,
|
||||||
|
);
|
||||||
|
await this.featureEnvironmentStore.disconnectProject(
|
||||||
|
environment,
|
||||||
|
projectId,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new MinimumOneEnvironmentError(
|
||||||
|
'You must always have one active environment',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,3 +85,20 @@ test('Should remove environment from project', async () => {
|
|||||||
|
|
||||||
expect(envs).toHaveLength(1);
|
expect(envs).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Should not remove environment from project if project only has one environment enabled', async () => {
|
||||||
|
await app.request
|
||||||
|
.delete(`/api/admin/projects/default/environments/default`)
|
||||||
|
.expect(400)
|
||||||
|
.expect((r) => {
|
||||||
|
expect(r.body.details[0].message).toBe(
|
||||||
|
'You must always have one active environment',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const envs = await db.stores.projectStore.getEnvironmentsForProject(
|
||||||
|
'default',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(envs).toHaveLength(1);
|
||||||
|
});
|
||||||
|
@ -53,7 +53,12 @@ test('returns feature toggle for default env', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('returns feature toggle for default env even if it is removed from project', async () => {
|
test('returns feature toggle for default env even if it is removed from project', async () => {
|
||||||
await app.services.environmentService.removeEnvironmentFromProject(
|
await db.stores.featureEnvironmentStore.disconnectFeatures(
|
||||||
|
'default',
|
||||||
|
'default',
|
||||||
|
);
|
||||||
|
|
||||||
|
await db.stores.featureEnvironmentStore.disconnectProject(
|
||||||
'default',
|
'default',
|
||||||
'default',
|
'default',
|
||||||
);
|
);
|
||||||
|
@ -109,10 +109,11 @@ test('Adding same environment twice should throw a NameExistsError', async () =>
|
|||||||
name: 'uniqueness-test',
|
name: 'uniqueness-test',
|
||||||
type: 'production',
|
type: 'production',
|
||||||
});
|
});
|
||||||
|
await service.addEnvironmentToProject('uniqueness-test', 'default');
|
||||||
|
|
||||||
await service.removeEnvironmentFromProject('test-connection', 'default');
|
await service.removeEnvironmentFromProject('test-connection', 'default');
|
||||||
await service.removeEnvironmentFromProject('removal-test', 'default');
|
await service.removeEnvironmentFromProject('removal-test', 'default');
|
||||||
|
|
||||||
await service.addEnvironmentToProject('uniqueness-test', 'default');
|
|
||||||
return expect(async () =>
|
return expect(async () =>
|
||||||
service.addEnvironmentToProject('uniqueness-test', 'default'),
|
service.addEnvironmentToProject('uniqueness-test', 'default'),
|
||||||
).rejects.toThrow(
|
).rejects.toThrow(
|
||||||
|
Loading…
Reference in New Issue
Block a user