mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-20 00:08:02 +01:00
feat: make application usage private through project (#4786)
This commit is contained in:
parent
0d7c230af9
commit
e4f8e1692a
@ -14,6 +14,7 @@ import {
|
|||||||
getStandardResponses,
|
getStandardResponses,
|
||||||
} from '../../openapi/util/standard-responses';
|
} from '../../openapi/util/standard-responses';
|
||||||
import { CreateApplicationSchema } from '../../openapi/spec/create-application-schema';
|
import { CreateApplicationSchema } from '../../openapi/spec/create-application-schema';
|
||||||
|
import { IAuthRequest } from '../unleash-types';
|
||||||
|
|
||||||
class MetricsController extends Controller {
|
class MetricsController extends Controller {
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
@ -148,14 +149,16 @@ class MetricsController extends Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getApplications(
|
async getApplications(
|
||||||
req: Request,
|
req: IAuthRequest,
|
||||||
res: Response<ApplicationsSchema>,
|
res: Response<ApplicationsSchema>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const { user } = req;
|
||||||
const query = req.query.strategyName
|
const query = req.query.strategyName
|
||||||
? { strategyName: req.query.strategyName as string }
|
? { strategyName: req.query.strategyName as string }
|
||||||
: {};
|
: {};
|
||||||
const applications = await this.clientInstanceService.getApplications(
|
const applications = await this.clientInstanceService.getApplications(
|
||||||
query,
|
query,
|
||||||
|
user.id,
|
||||||
);
|
);
|
||||||
res.json({ applications });
|
res.json({ applications });
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import { IClientApp } from '../../types/model';
|
|||||||
import { secondsToMilliseconds } from 'date-fns';
|
import { secondsToMilliseconds } from 'date-fns';
|
||||||
import FakeEventStore from '../../../test/fixtures/fake-event-store';
|
import FakeEventStore from '../../../test/fixtures/fake-event-store';
|
||||||
import { createTestConfig } from '../../../test/config/test-config';
|
import { createTestConfig } from '../../../test/config/test-config';
|
||||||
|
import { FakePrivateProjectChecker } from '../../features/private-project/fakePrivateProjectChecker';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A utility to wait for any pending promises in the test subject code.
|
* A utility to wait for any pending promises in the test subject code.
|
||||||
@ -61,6 +62,7 @@ test('Multiple registrations of same appname and instanceid within same time per
|
|||||||
eventStore: new FakeEventStore(),
|
eventStore: new FakeEventStore(),
|
||||||
},
|
},
|
||||||
config,
|
config,
|
||||||
|
new FakePrivateProjectChecker(),
|
||||||
);
|
);
|
||||||
const client1: IClientApp = {
|
const client1: IClientApp = {
|
||||||
appName: 'test_app',
|
appName: 'test_app',
|
||||||
@ -111,6 +113,7 @@ test('Multiple unique clients causes multiple registrations', async () => {
|
|||||||
eventStore: new FakeEventStore(),
|
eventStore: new FakeEventStore(),
|
||||||
},
|
},
|
||||||
config,
|
config,
|
||||||
|
new FakePrivateProjectChecker(),
|
||||||
);
|
);
|
||||||
const client1 = {
|
const client1 = {
|
||||||
appName: 'test_app',
|
appName: 'test_app',
|
||||||
@ -164,6 +167,7 @@ test('Same client registered outside of dedup interval will be registered twice'
|
|||||||
eventStore: new FakeEventStore(),
|
eventStore: new FakeEventStore(),
|
||||||
},
|
},
|
||||||
config,
|
config,
|
||||||
|
new FakePrivateProjectChecker(),
|
||||||
bulkInterval,
|
bulkInterval,
|
||||||
);
|
);
|
||||||
const client1 = {
|
const client1 = {
|
||||||
@ -217,6 +221,7 @@ test('No registrations during a time period will not call stores', async () => {
|
|||||||
eventStore: new FakeEventStore(),
|
eventStore: new FakeEventStore(),
|
||||||
},
|
},
|
||||||
config,
|
config,
|
||||||
|
new FakePrivateProjectChecker(),
|
||||||
);
|
);
|
||||||
jest.advanceTimersByTime(6000);
|
jest.advanceTimersByTime(6000);
|
||||||
expect(appStoreSpy).toHaveBeenCalledTimes(0);
|
expect(appStoreSpy).toHaveBeenCalledTimes(0);
|
||||||
|
@ -18,6 +18,8 @@ import { minutesToMilliseconds, secondsToMilliseconds } from 'date-fns';
|
|||||||
import { IClientMetricsStoreV2 } from '../../types/stores/client-metrics-store-v2';
|
import { IClientMetricsStoreV2 } from '../../types/stores/client-metrics-store-v2';
|
||||||
import { clientMetricsSchema } from './schema';
|
import { clientMetricsSchema } from './schema';
|
||||||
import { PartialSome } from '../../types/partial';
|
import { PartialSome } from '../../types/partial';
|
||||||
|
import { IPrivateProjectChecker } from '../../features/private-project/privateProjectCheckerType';
|
||||||
|
import { IFlagResolver } from '../../types';
|
||||||
|
|
||||||
export default class ClientInstanceService {
|
export default class ClientInstanceService {
|
||||||
apps = {};
|
apps = {};
|
||||||
@ -40,6 +42,10 @@ export default class ClientInstanceService {
|
|||||||
|
|
||||||
private eventStore: IEventStore;
|
private eventStore: IEventStore;
|
||||||
|
|
||||||
|
private privateProjectChecker: IPrivateProjectChecker;
|
||||||
|
|
||||||
|
private flagResolver: IFlagResolver;
|
||||||
|
|
||||||
private bulkInterval: number;
|
private bulkInterval: number;
|
||||||
|
|
||||||
private announcementInterval: number;
|
private announcementInterval: number;
|
||||||
@ -61,7 +67,11 @@ export default class ClientInstanceService {
|
|||||||
| 'clientInstanceStore'
|
| 'clientInstanceStore'
|
||||||
| 'eventStore'
|
| 'eventStore'
|
||||||
>,
|
>,
|
||||||
{ getLogger }: Pick<IUnleashConfig, 'getLogger'>,
|
{
|
||||||
|
getLogger,
|
||||||
|
flagResolver,
|
||||||
|
}: Pick<IUnleashConfig, 'getLogger' | 'flagResolver'>,
|
||||||
|
privateProjectChecker: IPrivateProjectChecker,
|
||||||
bulkInterval = secondsToMilliseconds(5),
|
bulkInterval = secondsToMilliseconds(5),
|
||||||
announcementInterval = minutesToMilliseconds(5),
|
announcementInterval = minutesToMilliseconds(5),
|
||||||
) {
|
) {
|
||||||
@ -71,6 +81,8 @@ export default class ClientInstanceService {
|
|||||||
this.clientApplicationsStore = clientApplicationsStore;
|
this.clientApplicationsStore = clientApplicationsStore;
|
||||||
this.clientInstanceStore = clientInstanceStore;
|
this.clientInstanceStore = clientInstanceStore;
|
||||||
this.eventStore = eventStore;
|
this.eventStore = eventStore;
|
||||||
|
this.privateProjectChecker = privateProjectChecker;
|
||||||
|
this.flagResolver = flagResolver;
|
||||||
this.logger = getLogger(
|
this.logger = getLogger(
|
||||||
'/services/client-metrics/client-instance-service.ts',
|
'/services/client-metrics/client-instance-service.ts',
|
||||||
);
|
);
|
||||||
@ -162,8 +174,27 @@ export default class ClientInstanceService {
|
|||||||
|
|
||||||
async getApplications(
|
async getApplications(
|
||||||
query: IApplicationQuery,
|
query: IApplicationQuery,
|
||||||
|
userId: number,
|
||||||
): Promise<IClientApplication[]> {
|
): Promise<IClientApplication[]> {
|
||||||
return this.clientApplicationsStore.getAppsForStrategy(query);
|
const applications =
|
||||||
|
await this.clientApplicationsStore.getAppsForStrategy(query);
|
||||||
|
if (this.flagResolver.isEnabled('privateProjects') && userId) {
|
||||||
|
const accessibleProjects =
|
||||||
|
await this.privateProjectChecker.getUserAccessibleProjects(
|
||||||
|
userId,
|
||||||
|
);
|
||||||
|
return applications.map((application) => {
|
||||||
|
return {
|
||||||
|
...application,
|
||||||
|
usage: application.usage?.filter(
|
||||||
|
(usageItem) =>
|
||||||
|
usageItem.project === '*' ||
|
||||||
|
accessibleProjects.includes(usageItem.project),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return applications;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getApplication(appName: string): Promise<IApplication> {
|
async getApplication(appName: string): Promise<IApplication> {
|
||||||
|
@ -162,7 +162,6 @@ export const createServices = (
|
|||||||
const groupService = new GroupService(stores, config);
|
const groupService = new GroupService(stores, config);
|
||||||
const accessService = new AccessService(stores, config, groupService);
|
const accessService = new AccessService(stores, config, groupService);
|
||||||
const apiTokenService = new ApiTokenService(stores, config);
|
const apiTokenService = new ApiTokenService(stores, config);
|
||||||
const clientInstanceService = new ClientInstanceService(stores, config);
|
|
||||||
const lastSeenService = new LastSeenService(stores, config);
|
const lastSeenService = new LastSeenService(stores, config);
|
||||||
const clientMetricsServiceV2 = new ClientMetricsServiceV2(
|
const clientMetricsServiceV2 = new ClientMetricsServiceV2(
|
||||||
stores,
|
stores,
|
||||||
@ -205,6 +204,11 @@ export const createServices = (
|
|||||||
const privateProjectChecker = db
|
const privateProjectChecker = db
|
||||||
? createPrivateProjectChecker(db, config)
|
? createPrivateProjectChecker(db, config)
|
||||||
: createFakePrivateProjectChecker();
|
: createFakePrivateProjectChecker();
|
||||||
|
const clientInstanceService = new ClientInstanceService(
|
||||||
|
stores,
|
||||||
|
config,
|
||||||
|
privateProjectChecker,
|
||||||
|
);
|
||||||
const featureToggleServiceV2 = new FeatureToggleService(
|
const featureToggleServiceV2 = new FeatureToggleService(
|
||||||
stores,
|
stores,
|
||||||
config,
|
config,
|
||||||
|
@ -43,7 +43,7 @@ process.nextTick(async () => {
|
|||||||
featureNamingPattern: true,
|
featureNamingPattern: true,
|
||||||
doraMetrics: true,
|
doraMetrics: true,
|
||||||
variantTypeNumber: true,
|
variantTypeNumber: true,
|
||||||
privateProjects: false,
|
privateProjects: true,
|
||||||
accessOverview: true,
|
accessOverview: true,
|
||||||
datadogJsonTemplate: true,
|
datadogJsonTemplate: true,
|
||||||
},
|
},
|
||||||
|
@ -3,6 +3,7 @@ import { IClientApp } from '../../../lib/types/model';
|
|||||||
import { secondsToMilliseconds } from 'date-fns';
|
import { secondsToMilliseconds } from 'date-fns';
|
||||||
import { createTestConfig } from '../../config/test-config';
|
import { createTestConfig } from '../../config/test-config';
|
||||||
import { IUnleashConfig } from '../../../lib/types';
|
import { IUnleashConfig } from '../../../lib/types';
|
||||||
|
import { FakePrivateProjectChecker } from '../../../lib/features/private-project/fakePrivateProjectChecker';
|
||||||
|
|
||||||
const faker = require('faker');
|
const faker = require('faker');
|
||||||
const dbInit = require('../helpers/database-init');
|
const dbInit = require('../helpers/database-init');
|
||||||
@ -23,6 +24,7 @@ beforeAll(async () => {
|
|||||||
clientInstanceService = new ClientInstanceService(
|
clientInstanceService = new ClientInstanceService(
|
||||||
stores,
|
stores,
|
||||||
config,
|
config,
|
||||||
|
new FakePrivateProjectChecker(),
|
||||||
bulkInterval,
|
bulkInterval,
|
||||||
announcementInterval,
|
announcementInterval,
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user