1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

feat: ability to configure when users are considered inactive (#8454)

Give the ability to change when users are considered inactive via an
environment variable `USER_INACTIVITY_THRESHOLD_IN_DAYS` or
configuration option: `userInactivityThresholdInDays`. Default remains
180 days
This commit is contained in:
Gastón Fournier 2024-10-15 16:34:04 +02:00 committed by GitHub
parent 6b56f8ff89
commit 7fb9308b3e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 32 additions and 11 deletions

View File

@ -154,6 +154,7 @@ exports[`should create default config 1`] = `
"ui": { "ui": {
"environment": "Open Source", "environment": "Open Source",
}, },
"userInactivityThresholdInDays": 180,
"versionCheck": { "versionCheck": {
"enable": true, "enable": true,
"url": "https://version.unleash.run", "url": "https://version.unleash.run",

View File

@ -713,6 +713,14 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig {
const openAIAPIKey = process.env.OPENAI_API_KEY; const openAIAPIKey = process.env.OPENAI_API_KEY;
const defaultDaysToBeConsideredInactive = 180;
const userInactivityThresholdInDays =
options.userInactivityThresholdInDays ??
parseEnvVarNumber(
process.env.USER_INACTIVITY_THRESHOLD_IN_DAYS,
defaultDaysToBeConsideredInactive,
);
return { return {
db, db,
session, session,
@ -752,6 +760,7 @@ export function createConfig(options: IUnleashOptions): IUnleashConfig {
feedbackUriPath, feedbackUriPath,
dailyMetricsStorageDays, dailyMetricsStorageDays,
openAIAPIKey, openAIAPIKey,
userInactivityThresholdInDays,
}; };
} }

View File

@ -156,6 +156,7 @@ export interface IUnleashOptions {
| 'segments' | 'segments'
> >
>; >;
userInactivityThresholdInDays?: number;
} }
export interface IEmailOption { export interface IEmailOption {
@ -274,4 +275,5 @@ export interface IUnleashConfig {
rateLimiting: IRateLimiting; rateLimiting: IRateLimiting;
feedbackUriPath?: string; feedbackUriPath?: string;
openAIAPIKey?: string; openAIAPIKey?: string;
userInactivityThresholdInDays: number;
} }

View File

@ -5,30 +5,32 @@ import { InactiveUsersStore } from './inactive-users-store';
import { FakeInactiveUsersStore } from './fakes/fake-inactive-users-store'; import { FakeInactiveUsersStore } from './fakes/fake-inactive-users-store';
import type { UserService } from '../../services'; import type { UserService } from '../../services';
export const DAYS_TO_BE_COUNTED_AS_INACTIVE = 180;
export const createInactiveUsersService = ( export const createInactiveUsersService = (
db: Db, db: Db,
config: IUnleashConfig, config: IUnleashConfig,
userService: UserService, userService: UserService,
): InactiveUsersService => { ): InactiveUsersService => {
const { eventBus, getLogger } = config; const { eventBus, getLogger, userInactivityThresholdInDays } = config;
const inactiveUsersStore = new InactiveUsersStore(db, eventBus, getLogger); const inactiveUsersStore = new InactiveUsersStore(db, eventBus, getLogger);
return new InactiveUsersService( return new InactiveUsersService(
{ inactiveUsersStore }, { inactiveUsersStore },
{ getLogger }, { getLogger, userInactivityThresholdInDays },
{ userService }, { userService },
); );
}; };
export const createFakeInactiveUsersService = ( export const createFakeInactiveUsersService = (
{ getLogger, eventBus }: Pick<IUnleashConfig, 'getLogger' | 'eventBus'>, {
getLogger,
userInactivityThresholdInDays,
}: Pick<IUnleashConfig, 'getLogger' | 'userInactivityThresholdInDays'>,
userService: UserService, userService: UserService,
): InactiveUsersService => { ): InactiveUsersService => {
const fakeStore = new FakeInactiveUsersStore(); const fakeStore = new FakeInactiveUsersStore();
return new InactiveUsersService( return new InactiveUsersService(
{ inactiveUsersStore: fakeStore }, { inactiveUsersStore: fakeStore },
{ getLogger }, { getLogger, userInactivityThresholdInDays },
{ userService }, { userService },
); );
}; };

View File

@ -20,7 +20,6 @@ import {
import type { IAuthRequest } from '../../routes/unleash-types'; import type { IAuthRequest } from '../../routes/unleash-types';
import type { Response } from 'express'; import type { Response } from 'express';
import type { OpenApiService } from '../../services'; import type { OpenApiService } from '../../services';
import { DAYS_TO_BE_COUNTED_AS_INACTIVE } from './createInactiveUsersService';
import { anonymise } from '../../util'; import { anonymise } from '../../util';
export class InactiveUsersController extends Controller { export class InactiveUsersController extends Controller {
private readonly logger: Logger; private readonly logger: Logger;
@ -30,6 +29,8 @@ export class InactiveUsersController extends Controller {
private openApiService: OpenApiService; private openApiService: OpenApiService;
private flagResolver: IFlagResolver; private flagResolver: IFlagResolver;
private readonly userInactivityThresholdInDays: number;
constructor( constructor(
config: IUnleashConfig, config: IUnleashConfig,
{ {
@ -44,6 +45,8 @@ export class InactiveUsersController extends Controller {
this.inactiveUsersService = inactiveUsersService; this.inactiveUsersService = inactiveUsersService;
this.openApiService = openApiService; this.openApiService = openApiService;
this.flagResolver = config.flagResolver; this.flagResolver = config.flagResolver;
this.userInactivityThresholdInDays =
config.userInactivityThresholdInDays;
this.route({ this.route({
method: 'get', method: 'get',
@ -54,7 +57,7 @@ export class InactiveUsersController extends Controller {
openApiService.validPath({ openApiService.validPath({
operationId: 'getInactiveUsers', operationId: 'getInactiveUsers',
summary: 'Gets inactive users', summary: 'Gets inactive users',
description: `Gets all inactive users. An inactive user is a user that has not logged in in the last ${DAYS_TO_BE_COUNTED_AS_INACTIVE} days`, description: `Gets all inactive users. An inactive user is a user that has not logged in in the last ${this.userInactivityThresholdInDays} days`,
tags: ['Users'], tags: ['Users'],
responses: { responses: {
200: createResponseSchema('inactiveUsersSchema'), 200: createResponseSchema('inactiveUsersSchema'),
@ -71,7 +74,7 @@ export class InactiveUsersController extends Controller {
openApiService.validPath({ openApiService.validPath({
operationId: 'deleteInactiveUsers', operationId: 'deleteInactiveUsers',
summary: 'Deletes inactive users', summary: 'Deletes inactive users',
description: `Deletes all inactive users. An inactive user is a user that has not logged in in the last ${DAYS_TO_BE_COUNTED_AS_INACTIVE} days`, description: `Deletes all inactive users. An inactive user is a user that has not logged in in the last ${this.userInactivityThresholdInDays} days`,
tags: ['Users'], tags: ['Users'],
requestBody: createRequestSchema('idsSchema'), requestBody: createRequestSchema('idsSchema'),
responses: { responses: {

View File

@ -8,15 +8,18 @@ import type { IInactiveUsersStore } from './types/inactive-users-store-type';
import type { Logger } from '../../logger'; import type { Logger } from '../../logger';
import type { InactiveUserSchema } from '../../openapi'; import type { InactiveUserSchema } from '../../openapi';
import type { UserService } from '../../services'; import type { UserService } from '../../services';
import { DAYS_TO_BE_COUNTED_AS_INACTIVE } from './createInactiveUsersService';
export class InactiveUsersService { export class InactiveUsersService {
private inactiveUsersStore: IInactiveUsersStore; private inactiveUsersStore: IInactiveUsersStore;
private readonly logger: Logger; private readonly logger: Logger;
private userService: UserService; private userService: UserService;
private readonly userInactivityThresholdInDays: number;
constructor( constructor(
{ inactiveUsersStore }: Pick<IUnleashStores, 'inactiveUsersStore'>, { inactiveUsersStore }: Pick<IUnleashStores, 'inactiveUsersStore'>,
{ getLogger }: Pick<IUnleashConfig, 'getLogger'>, {
getLogger,
userInactivityThresholdInDays,
}: Pick<IUnleashConfig, 'getLogger' | 'userInactivityThresholdInDays'>,
services: { services: {
userService: UserService; userService: UserService;
}, },
@ -24,11 +27,12 @@ export class InactiveUsersService {
this.logger = getLogger('services/client-feature-toggle-service.ts'); this.logger = getLogger('services/client-feature-toggle-service.ts');
this.inactiveUsersStore = inactiveUsersStore; this.inactiveUsersStore = inactiveUsersStore;
this.userService = services.userService; this.userService = services.userService;
this.userInactivityThresholdInDays = userInactivityThresholdInDays;
} }
async getInactiveUsers(): Promise<InactiveUserSchema[]> { async getInactiveUsers(): Promise<InactiveUserSchema[]> {
const users = await this.inactiveUsersStore.getInactiveUsers( const users = await this.inactiveUsersStore.getInactiveUsers(
DAYS_TO_BE_COUNTED_AS_INACTIVE, this.userInactivityThresholdInDays,
); );
if (users.length > 0) { if (users.length > 0) {
return users.map((user) => { return users.map((user) => {