-
-
Notifications
You must be signed in to change notification settings - Fork 740
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix/last seen at by environment #4939
Changes from 11 commits
960a6d1
5e95ffc
93d4ffd
1e65a1a
03c8364
3d4991d
0fe9c75
9a961a7
d93d6a5
766d1e3
83ece07
2407520
8742f5c
7d14af5
98f5629
a474998
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import FakeFeatureToggleStore from '../../../../test/fixtures/fake-feature-toggle-store'; | ||
import FeatureToggleStore from '../../../db/feature-toggle-store'; | ||
import { Db, IUnleashConfig } from '../../../server-impl'; | ||
import { FakeLastSeenStore } from './fake-last-seen-store'; | ||
import { LastSeenService } from './last-seen-service'; | ||
import LastSeenStore from './last-seen-store'; | ||
|
||
export const createLastSeenService = ( | ||
db: Db, | ||
config: IUnleashConfig, | ||
): LastSeenService => { | ||
const lastSeenStore = new LastSeenStore( | ||
db, | ||
config.eventBus, | ||
config.getLogger, | ||
); | ||
|
||
const featureToggleStore = new FeatureToggleStore( | ||
db, | ||
config.eventBus, | ||
config.getLogger, | ||
); | ||
|
||
return new LastSeenService({ lastSeenStore, featureToggleStore }, config); | ||
}; | ||
|
||
export const createFakeLastSeenService = ( | ||
config: IUnleashConfig, | ||
): LastSeenService => { | ||
const lastSeenStore = new FakeLastSeenStore(); | ||
const featureToggleStore = new FakeFeatureToggleStore(); | ||
|
||
return new LastSeenService({ lastSeenStore, featureToggleStore }, config); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { IFeatureLastSeenResults } from './last-seen-read-model'; | ||
import { ILastSeenReadModel } from './types/last-seen-read-model-type'; | ||
|
||
export class FakeLastSeenReadModel implements ILastSeenReadModel { | ||
// eslint-disable-next-line | ||
getForFeature(features: string[]): Promise<IFeatureLastSeenResults> { | ||
return Promise.resolve({}); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { LastSeenInput } from './last-seen-service'; | ||
import { ILastSeenStore } from './types/last-seen-store-type'; | ||
|
||
export class FakeLastSeenStore implements ILastSeenStore { | ||
setLastSeen(data: LastSeenInput[]): Promise<void> { | ||
data.map((lastSeen) => lastSeen); | ||
return Promise.resolve(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { Logger } from '../../../logger'; | ||
import { IFeatureOverview } from '../../../types'; | ||
import { IFeatureLastSeenResults } from './last-seen-read-model'; | ||
|
||
export class LastSeenMapper { | ||
mapToFeatures( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Map results of reading into environments of incoming features to maintain todays data structure, might change in follow up iteration |
||
features: IFeatureOverview[], | ||
lastSeenAtPerEnvironment: IFeatureLastSeenResults, | ||
logger: Logger, | ||
): IFeatureOverview[] { | ||
return features.map((feature) => { | ||
if (!feature.environments) { | ||
logger.warn('Feature without environments:', feature); | ||
return feature; | ||
} | ||
|
||
feature.environments = feature.environments.map((environment) => { | ||
const noData = | ||
!lastSeenAtPerEnvironment[feature.name] || | ||
!lastSeenAtPerEnvironment[feature.name][environment.name]; | ||
|
||
if (noData) { | ||
logger.warn( | ||
'No last seen data for environment:', | ||
environment, | ||
); | ||
return environment; | ||
} | ||
|
||
environment.lastSeenAt = new Date( | ||
lastSeenAtPerEnvironment[feature.name][environment.name] | ||
.lastSeen, | ||
); | ||
return environment; | ||
}); | ||
return feature; | ||
}); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { Db } from '../../../db/db'; | ||
import { ILastSeenReadModel } from './types/last-seen-read-model-type'; | ||
|
||
const TABLE = 'last_seen_at_metrics'; | ||
|
||
export interface IFeatureLastSeenResults { | ||
[featureName: string]: { | ||
[environment: string]: { | ||
lastSeen: string; | ||
}; | ||
}; | ||
} | ||
export class LastSeenAtReadModel implements ILastSeenReadModel { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. retrieve last seen at results per environment for an array of features |
||
private db: Db; | ||
|
||
constructor(db: Db) { | ||
this.db = db; | ||
} | ||
|
||
async getForFeature(features: string[]): Promise<IFeatureLastSeenResults> { | ||
const rows = await this.db(TABLE).whereIn('feature_name', features); | ||
|
||
const result = rows.reduce((acc, curr) => { | ||
if (!acc[curr.feature_name]) { | ||
acc[curr.feature_name] = {}; | ||
|
||
acc[curr.feature_name][curr.environment] = { | ||
lastSeen: curr.last_seen_at, | ||
}; | ||
} else { | ||
acc[curr.feature_name][curr.environment] = { | ||
lastSeen: curr.last_seen_at, | ||
}; | ||
} | ||
|
||
return acc; | ||
}, {}); | ||
|
||
return result; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import createStores from '../../../../test/fixtures/store'; | ||
import EventEmitter from 'events'; | ||
import getLogger from '../../../../test/fixtures/no-logger'; | ||
import { IUnleashConfig } from '../../../types'; | ||
|
||
import { createFakeLastSeenService } from './createLastSeenService'; | ||
|
||
function initLastSeenService(flagEnabled = true) { | ||
const stores = createStores(); | ||
|
||
const eventBus = new EventEmitter(); | ||
eventBus.emit = jest.fn(); | ||
|
||
const config = { | ||
eventBus, | ||
getLogger, | ||
flagResolver: { | ||
isEnabled: () => { | ||
return flagEnabled; | ||
}, | ||
}, | ||
} as unknown as IUnleashConfig; | ||
|
||
const lastSeenService = createFakeLastSeenService(config); | ||
|
||
return { lastSeenService, featureToggleStore: stores.featureToggleStore }; | ||
} | ||
|
||
test('should not add duplicates per feature/environment', async () => { | ||
const { lastSeenService, featureToggleStore } = initLastSeenService(); | ||
|
||
lastSeenService.updateLastSeen([ | ||
{ | ||
featureName: 'myFeature', | ||
environment: 'development', | ||
yes: 1, | ||
no: 0, | ||
appName: 'test', | ||
timestamp: new Date(), | ||
}, | ||
]); | ||
|
||
lastSeenService.updateLastSeen([ | ||
{ | ||
featureName: 'myFeature', | ||
environment: 'development', | ||
yes: 1, | ||
no: 0, | ||
appName: 'test', | ||
timestamp: new Date(), | ||
}, | ||
]); | ||
featureToggleStore.setLastSeen = jest.fn(); | ||
await lastSeenService.store(); | ||
|
||
expect(featureToggleStore.setLastSeen).toHaveBeenCalledWith([ | ||
{ | ||
environment: 'development', | ||
featureName: 'myFeature', | ||
}, | ||
]); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use composite root pattern for LastSeenService