-
-
Notifications
You must be signed in to change notification settings - Fork 656
/
global-frontend-api-cache.ts
115 lines (97 loc) · 4 KB
/
global-frontend-api-cache.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import EventEmitter from 'events';
import { Segment } from 'unleash-client/lib/strategy/strategy';
import { FeatureInterface } from 'unleash-client/lib/feature';
import { IApiUser } from '../types/api-user';
import { ISegmentReadModel, IUnleashConfig } from '../types';
import {
mapFeaturesForClient,
mapSegmentsForClient,
} from '../features/playground/offline-unleash-client';
import { ALL_ENVS } from '../util/constants';
import { Logger } from '../logger';
import { UPDATE_REVISION } from '../features/feature-toggle/configuration-revision-service';
import { mapValues } from '../util';
import { IClientFeatureToggleReadModel } from './client-feature-toggle-read-model-type';
type Config = Pick<IUnleashConfig, 'getLogger'>;
export type GlobalFrontendApiCacheState = 'starting' | 'ready' | 'updated';
export class GlobalFrontendApiCache extends EventEmitter {
private readonly config: Config;
private readonly logger: Logger;
private readonly clientFeatureToggleReadModel: IClientFeatureToggleReadModel;
private readonly segmentReadModel: ISegmentReadModel;
private readonly configurationRevisionService: EventEmitter;
private featuresByEnvironment: Record<string, FeatureInterface[]> = {};
private segments: Segment[] = [];
private status: GlobalFrontendApiCacheState = 'starting';
constructor(
config: Config,
segmentReadModel: ISegmentReadModel,
clientFeatureToggleReadModel: IClientFeatureToggleReadModel,
configurationRevisionService: EventEmitter,
) {
super();
this.config = config;
this.logger = config.getLogger('proxy-repository.ts');
this.clientFeatureToggleReadModel = clientFeatureToggleReadModel;
this.configurationRevisionService = configurationRevisionService;
this.segmentReadModel = segmentReadModel;
this.onUpdateRevisionEvent = this.onUpdateRevisionEvent.bind(this);
this.refreshData();
this.configurationRevisionService.on(
UPDATE_REVISION,
this.onUpdateRevisionEvent,
);
}
getSegment(id: number): Segment | undefined {
return this.segments.find((segment) => segment.id === id);
}
getToggles(token: IApiUser): FeatureInterface[] {
if (
this.featuresByEnvironment[this.environmentNameForToken(token)] ==
null
)
return [];
return this.featuresByEnvironment[
this.environmentNameForToken(token)
].filter(
(feature) =>
token.projects.includes('*') ||
(feature.project && token.projects.includes(feature.project)),
);
}
private async getAllFeatures(): Promise<
Record<string, FeatureInterface[]>
> {
const features = await this.clientFeatureToggleReadModel.getClient();
return mapValues(features, mapFeaturesForClient);
}
private async getAllSegments(): Promise<Segment[]> {
return mapSegmentsForClient(await this.segmentReadModel.getAll());
}
// TODO: fetch only relevant projects/environments based on tokens
// TODO: also consider not fetching disabled features, because those are not returned by frontend API
private async refreshData() {
try {
this.featuresByEnvironment = await this.getAllFeatures();
this.segments = await this.getAllSegments();
if (this.status === 'starting') {
this.status = 'ready';
this.emit('ready');
} else if (this.status === 'ready' || this.status === 'updated') {
this.status = 'updated';
this.emit('updated');
}
} catch (e) {
this.logger.error('Cannot load data for token', e);
}
}
private async onUpdateRevisionEvent() {
await this.refreshData();
}
private environmentNameForToken(token: IApiUser): string {
if (token.environment === ALL_ENVS) {
return 'default';
}
return token.environment;
}
}