From 875fb7734cd982ea5017b9eae04d4cd0470b05fc Mon Sep 17 00:00:00 2001 From: Youssef Date: Mon, 8 Nov 2021 16:31:38 +0100 Subject: [PATCH 01/13] create splash migration store and service file --- src/lib/db/user-splash-store.ts | 111 ++++++++++++++++++ src/lib/services/user-splash-service.ts | 47 ++++++++ src/lib/types/stores.ts | 3 + src/lib/types/stores/user-splash-store.ts | 19 +++ ...20211108130333-create-user-splash-table.js | 25 ++++ src/server-dev.ts | 2 +- 6 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 src/lib/db/user-splash-store.ts create mode 100644 src/lib/services/user-splash-service.ts create mode 100644 src/lib/types/stores/user-splash-store.ts create mode 100644 src/migrations/20211108130333-create-user-splash-table.js diff --git a/src/lib/db/user-splash-store.ts b/src/lib/db/user-splash-store.ts new file mode 100644 index 0000000000..77ddbc1c05 --- /dev/null +++ b/src/lib/db/user-splash-store.ts @@ -0,0 +1,111 @@ +import { Knex } from 'knex'; +import { EventEmitter } from 'events'; +import { LogProvider, Logger } from '../logger'; +import { + IUserSplash, + IUserSplashKey, + IUserSplashStore, +} from '../types/stores/user-splash-store'; + +const COLUMNS = ['user_id', 'splash_id', 'seen']; +const TABLE = 'user_splash'; + +interface IUserSplashTable { + seen?: boolean; + splash_id: string; + user_id: number; +} + +const fieldToRow = (fields: IUserSplash): IUserSplashTable => ({ + seen: fields.seen, + splash_id: fields.splashId, + user_id: fields.userId, +}); + +const rowToField = (row: IUserSplashTable): IUserSplash => ({ + seen: row.seen, + splashId: row.splash_id, + userId: row.user_id, +}); + +export default class UserSplashStore implements IUserSplashStore { + private db: Knex; + + private logger: Logger; + + constructor(db: Knex, eventBus: EventEmitter, getLogger: LogProvider) { + this.db = db; + this.logger = getLogger('user-splash-store.ts'); + } + + async getAllUserSplashs(userId: number): Promise { + const userSplash = await this.db + .table(TABLE) + .select() + .where({ user_id: userId }); + + return userSplash.map(rowToField); + } + + async getSplash( + userId: number, + splashId: string, + ): Promise { + const userSplash = await this.db + .table(TABLE) + .select() + .where({ user_id: userId, splash_id: splashId }) + .first(); + + return rowToField(userSplash); + } + + async updateSplash(splash: IUserSplash): Promise { + const insertedSplash = await this.db + .table(TABLE) + .insert(fieldToRow(splash)) + .onConflict(['user_id', 'splash_id']) + .merge() + .returning(COLUMNS); + + return rowToField(insertedSplash[0]); + } + + async delete({ userId, splashId }: IUserSplashKey): Promise { + await this.db(TABLE) + .where({ user_id: userId, splash_id: splashId }) + .del(); + } + + async deleteAll(): Promise { + await this.db(TABLE).del(); + } + + destroy(): void {} + + async exists({ userId, splashId }: IUserSplashKey): Promise { + const result = await this.db.raw( + `SELECT EXISTS (SELECT 1 FROM ${TABLE} WHERE user_id = ? AND splash_id = ?) AS present`, + [userId, splashId], + ); + const { present } = result.rows[0]; + return present; + } + + async get({ + userId, + splashId, + }: IUserSplashKey): Promise { + return this.getSplash(userId, splashId); + } + + async getAll(): Promise { + const userSplashs = await this.db + .table(TABLE) + .select(); + + return userSplashs.map(rowToField); + } +} + +module.exports = UserSplashStore; diff --git a/src/lib/services/user-splash-service.ts b/src/lib/services/user-splash-service.ts new file mode 100644 index 0000000000..a86b44f009 --- /dev/null +++ b/src/lib/services/user-splash-service.ts @@ -0,0 +1,47 @@ +import { Logger } from '../logger'; +import { IUnleashStores } from '../types/stores'; +import { IUnleashConfig } from '../types/option'; +import User from '../types/user'; +import { + IUserSplash, + IUserSplashStore, +} from '../types/stores/user-splash-store'; + +export default class UserSplashService { + private userSplashStore: IUserSplashStore; + + private logger: Logger; + + constructor( + { userSplashStore }: Pick, + { getLogger }: Pick, + ) { + this.userSplashStore = userSplashStore; + this.logger = getLogger('services/user-splash-service.js'); + } + + async getAllUserSplash(user: User): Promise { + if (user.isAPI) { + return []; + } + try { + return await this.userSplashStore.getAllUserSplashs(user.id); + } catch (err) { + this.logger.error(err); + return []; + } + } + + async getSplash( + user_id: number, + splash_id: string, + ): Promise { + return this.userSplashStore.getSplash(user_id, splash_id); + } + + async updateSplash(splash: IUserSplash): Promise { + return this.userSplashStore.updateSplash(splash); + } +} + +module.exports = UserSplashService; diff --git a/src/lib/types/stores.ts b/src/lib/types/stores.ts index ff22f24b3f..ff09b606a5 100644 --- a/src/lib/types/stores.ts +++ b/src/lib/types/stores.ts @@ -23,6 +23,8 @@ import { IFeatureStrategiesStore } from './stores/feature-strategies-store'; import { IEnvironmentStore } from './stores/environment-store'; import { IFeatureToggleClientStore } from './stores/feature-toggle-client-store'; import { IClientMetricsStoreV2 } from './stores/client-metrics-store-v2'; +import { IUserSplashStore } from './stores/user-splash-store'; + export interface IUnleashStores { accessStore: IAccessStore; @@ -50,4 +52,5 @@ export interface IUnleashStores { tagTypeStore: ITagTypeStore; userFeedbackStore: IUserFeedbackStore; userStore: IUserStore; + userSplashStore: IUserSplashStore; } diff --git a/src/lib/types/stores/user-splash-store.ts b/src/lib/types/stores/user-splash-store.ts new file mode 100644 index 0000000000..ebe833e823 --- /dev/null +++ b/src/lib/types/stores/user-splash-store.ts @@ -0,0 +1,19 @@ +import { Store } from './store'; + +export interface IUserSplash { + seen?: boolean; + splashId: string; + userId: number; +} + +export interface IUserSplashKey { + userId: number; + splashId: string; +} + +export interface IUserSplashStore + extends Store { + getAllUserSplashs(userId: number): Promise; + getSplash(userId: number, splashId: string): Promise; + updateSplash(splash: IUserSplash): Promise; +} diff --git a/src/migrations/20211108130333-create-user-splash-table.js b/src/migrations/20211108130333-create-user-splash-table.js new file mode 100644 index 0000000000..067ec76bd3 --- /dev/null +++ b/src/migrations/20211108130333-create-user-splash-table.js @@ -0,0 +1,25 @@ +'use strict'; + +exports.up = function (db, cb) { + db.runSql( + ` + CREATE TABLE IF NOT EXISTS user_splash + (user_id INTEGER NOT NULL references users (id), + splash_id TEXT, + seen BOOLEAN NOT NULL DEFAULT false, + PRIMARY KEY (user_id, splash_id)); + CREATE INDEX user_splash_user_id_idx ON user_splash (user_id); + );`, + cb, + ); +}; + +exports.down = function (db, cb) { + db.runSql( + ` + DROP INDEX user_splash_user_id_idx; + DROP TABLE user_splash; +`, + cb, +); +}; diff --git a/src/server-dev.ts b/src/server-dev.ts index bf1cea9c95..34d7b1aec2 100644 --- a/src/server-dev.ts +++ b/src/server-dev.ts @@ -8,7 +8,7 @@ process.nextTick(async () => { createConfig({ db: { user: 'unleash_user', - password: 'passord', + password: 'some_password', host: 'localhost', port: 5432, database: 'unleash', From d73293c5762d9667571b3819efef79fa1e013629 Mon Sep 17 00:00:00 2001 From: Fredrik Oseberg Date: Tue, 9 Nov 2021 11:52:02 +0100 Subject: [PATCH 02/13] fix: add migration --- src/lib/db/index.ts | 2 + src/lib/db/user-splash-store.ts | 10 +-- src/lib/routes/admin-api/user.ts | 6 ++ src/lib/services/index.ts | 3 + src/lib/services/user-splash-service.ts | 6 +- src/lib/types/services.ts | 2 + src/lib/types/stores/user-splash-store.ts | 5 +- ...20211108130333-create-user-splash-table.js | 11 ++-- ...211109103930-add-splash-entry-for-users.js | 15 +++++ src/server-dev.ts | 2 +- src/test/fixtures/fake-user-splash-store.ts | 65 +++++++++++++++++++ src/test/fixtures/store.ts | 2 + 12 files changed, 107 insertions(+), 22 deletions(-) create mode 100644 src/migrations/20211109103930-add-splash-entry-for-users.js create mode 100644 src/test/fixtures/fake-user-splash-store.ts diff --git a/src/lib/db/index.ts b/src/lib/db/index.ts index a7d0c07d04..fbe9cec78f 100644 --- a/src/lib/db/index.ts +++ b/src/lib/db/index.ts @@ -29,6 +29,7 @@ import EnvironmentStore from './environment-store'; import FeatureTagStore from './feature-tag-store'; import { FeatureEnvironmentStore } from './feature-environment-store'; import { ClientMetricsStoreV2 } from './client-metrics-store-v2'; +import UserSplashStore from './user-splash-store'; export const createStores = ( config: IUnleashConfig, @@ -85,6 +86,7 @@ export const createStores = ( eventBus, getLogger, ), + userSplashStore: new UserSplashStore(db, eventBus, getLogger), }; }; diff --git a/src/lib/db/user-splash-store.ts b/src/lib/db/user-splash-store.ts index 77ddbc1c05..6b867ee4d7 100644 --- a/src/lib/db/user-splash-store.ts +++ b/src/lib/db/user-splash-store.ts @@ -47,10 +47,7 @@ export default class UserSplashStore implements IUserSplashStore { return userSplash.map(rowToField); } - async getSplash( - userId: number, - splashId: string, - ): Promise { + async getSplash(userId: number, splashId: string): Promise { const userSplash = await this.db .table(TABLE) .select() @@ -92,10 +89,7 @@ export default class UserSplashStore implements IUserSplashStore { return present; } - async get({ - userId, - splashId, - }: IUserSplashKey): Promise { + async get({ userId, splashId }: IUserSplashKey): Promise { return this.getSplash(userId, splashId); } diff --git a/src/lib/routes/admin-api/user.ts b/src/lib/routes/admin-api/user.ts index 6b5299311f..1d2815a9c6 100644 --- a/src/lib/routes/admin-api/user.ts +++ b/src/lib/routes/admin-api/user.ts @@ -7,6 +7,7 @@ import { IUnleashServices } from '../../types/services'; import UserService from '../../services/user-service'; import SessionService from '../../services/session-service'; import UserFeedbackService from '../../services/user-feedback-service'; +import UserSplashService from 'lib/services/user-splash-service'; interface IChangeUserRequest { password: string; @@ -22,6 +23,8 @@ class UserController extends Controller { private sessionService: SessionService; + private userSplashService: UserSplashService; + constructor( config: IUnleashConfig, { @@ -29,12 +32,14 @@ class UserController extends Controller { userService, sessionService, userFeedbackService, + userSplashService, }: Pick< IUnleashServices, | 'accessService' | 'userService' | 'sessionService' | 'userFeedbackService' + | 'userSplashService' >, ) { super(config); @@ -57,6 +62,7 @@ class UserController extends Controller { const feedback = await this.userFeedbackService.getAllUserFeedback( user, ); + //const splash = await this.userSplashService.getAllUserSplash(user); // TODO: remove this line after we remove it from db. delete user.permissions; diff --git a/src/lib/services/index.ts b/src/lib/services/index.ts index 8e39e913cb..5d3ddc3e95 100644 --- a/src/lib/services/index.ts +++ b/src/lib/services/index.ts @@ -27,6 +27,7 @@ import FeatureToggleService from './feature-toggle-service'; import EnvironmentService from './environment-service'; import FeatureTagService from './feature-tag-service'; import ProjectHealthService from './project-health-service'; +import UserSplashService from './user-splash-service'; export const createServices = ( stores: IUnleashStores, @@ -72,6 +73,7 @@ export const createServices = ( accessService, featureToggleServiceV2, ); + const userSplashService = new UserSplashService(stores, config); return { accessService, @@ -100,6 +102,7 @@ export const createServices = ( userFeedbackService, featureTagService, projectHealthService, + userSplashService, }; }; diff --git a/src/lib/services/user-splash-service.ts b/src/lib/services/user-splash-service.ts index a86b44f009..9dfb163dd2 100644 --- a/src/lib/services/user-splash-service.ts +++ b/src/lib/services/user-splash-service.ts @@ -28,14 +28,12 @@ export default class UserSplashService { return await this.userSplashStore.getAllUserSplashs(user.id); } catch (err) { this.logger.error(err); + return []; } } - async getSplash( - user_id: number, - splash_id: string, - ): Promise { + async getSplash(user_id: number, splash_id: string): Promise { return this.userSplashStore.getSplash(user_id, splash_id); } diff --git a/src/lib/types/services.ts b/src/lib/types/services.ts index b2528df9f8..52dab846bb 100644 --- a/src/lib/types/services.ts +++ b/src/lib/types/services.ts @@ -23,6 +23,7 @@ import EnvironmentService from '../services/environment-service'; import FeatureTagService from '../services/feature-tag-service'; import ProjectHealthService from '../services/project-health-service'; import ClientMetricsServiceV2 from '../services/client-metrics/client-metrics-service-v2'; +import UserSplashService from '../services/user-splash-service'; export interface IUnleashServices { accessService: AccessService; @@ -51,4 +52,5 @@ export interface IUnleashServices { userFeedbackService: UserFeedbackService; userService: UserService; versionService: VersionService; + userSplashService: UserSplashService; } diff --git a/src/lib/types/stores/user-splash-store.ts b/src/lib/types/stores/user-splash-store.ts index ebe833e823..75938981ef 100644 --- a/src/lib/types/stores/user-splash-store.ts +++ b/src/lib/types/stores/user-splash-store.ts @@ -1,7 +1,7 @@ import { Store } from './store'; export interface IUserSplash { - seen?: boolean; + seen: boolean; splashId: string; userId: number; } @@ -11,8 +11,7 @@ export interface IUserSplashKey { splashId: string; } -export interface IUserSplashStore - extends Store { +export interface IUserSplashStore extends Store { getAllUserSplashs(userId: number): Promise; getSplash(userId: number, splashId: string): Promise; updateSplash(splash: IUserSplash): Promise; diff --git a/src/migrations/20211108130333-create-user-splash-table.js b/src/migrations/20211108130333-create-user-splash-table.js index 067ec76bd3..21953415b3 100644 --- a/src/migrations/20211108130333-create-user-splash-table.js +++ b/src/migrations/20211108130333-create-user-splash-table.js @@ -8,18 +8,17 @@ exports.up = function (db, cb) { splash_id TEXT, seen BOOLEAN NOT NULL DEFAULT false, PRIMARY KEY (user_id, splash_id)); - CREATE INDEX user_splash_user_id_idx ON user_splash (user_id); - );`, + CREATE INDEX user_splash_user_id_idx ON user_splash (user_id);`, cb, ); }; exports.down = function (db, cb) { - db.runSql( - ` + db.runSql( + ` DROP INDEX user_splash_user_id_idx; DROP TABLE user_splash; `, - cb, -); + cb, + ); }; diff --git a/src/migrations/20211109103930-add-splash-entry-for-users.js b/src/migrations/20211109103930-add-splash-entry-for-users.js new file mode 100644 index 0000000000..8a34421415 --- /dev/null +++ b/src/migrations/20211109103930-add-splash-entry-for-users.js @@ -0,0 +1,15 @@ +exports.up = function (db, cb) { + db.runSql(`SELECT * FROM users`, (err, results) => { + results.rows.forEach((user) => { + db.runSql( + `INSERT INTO user_splash(splash_id, user_id, seen) VALUES (?, ?, ?)`, + ['environments', user.id, false], + ); + }); + cb(); + }); +}; + +exports.down = function (db, cb) { + db.runSql('DELETE FROM user_splash', cb); +}; diff --git a/src/server-dev.ts b/src/server-dev.ts index 34d7b1aec2..bf1cea9c95 100644 --- a/src/server-dev.ts +++ b/src/server-dev.ts @@ -8,7 +8,7 @@ process.nextTick(async () => { createConfig({ db: { user: 'unleash_user', - password: 'some_password', + password: 'passord', host: 'localhost', port: 5432, database: 'unleash', diff --git a/src/test/fixtures/fake-user-splash-store.ts b/src/test/fixtures/fake-user-splash-store.ts new file mode 100644 index 0000000000..2fb6af87b8 --- /dev/null +++ b/src/test/fixtures/fake-user-splash-store.ts @@ -0,0 +1,65 @@ +import { + IUserSplashKey, + IUserSplash, + IUserSplashStore, +} from 'lib/types/stores/user-splash-store'; + +export default class FakeUserSplashStore implements IUserSplashStore { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getAllUserSplashs(userId: number): Promise { + return Promise.resolve([]); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getSplash(userId: number, splashId: string): Promise { + return Promise.resolve({ seen: false, userId: 123, splashId: 'env' }); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + updateSplash(splash: IUserSplash): Promise { + return Promise.resolve({ seen: false, userId: 123, splashId: 'env' }); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + exists(key: IUserSplashKey): Promise { + return Promise.resolve(false); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + get(key: IUserSplashKey): Promise { + return Promise.resolve(undefined); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getAll(): Promise { + return Promise.resolve([]); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getAllUserFeedback(userId: number): Promise { + return Promise.resolve([]); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getFeedback(userId: number, feedbackId: string): Promise { + return Promise.resolve(undefined); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + updateFeedback(feedback: IUserSplash): Promise { + return Promise.resolve(undefined); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + delete(key: IUserSplashKey): Promise { + return Promise.resolve(undefined); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + deleteAll(): Promise { + return Promise.resolve(undefined); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + destroy(): void {} +} diff --git a/src/test/fixtures/store.ts b/src/test/fixtures/store.ts index 5e36e35ff0..6b6bf25d04 100644 --- a/src/test/fixtures/store.ts +++ b/src/test/fixtures/store.ts @@ -24,6 +24,7 @@ import FakeFeatureTypeStore from './fake-feature-type-store'; import FakeResetTokenStore from './fake-reset-token-store'; import FakeFeatureToggleClientStore from './fake-feature-toggle-client-store'; import FakeClientMetricsStoreV2 from './fake-client-metrics-store-v2'; +import FakeUserSplashStore from './fake-user-splash-store'; const createStores: () => IUnleashStores = () => { const db = { @@ -59,6 +60,7 @@ const createStores: () => IUnleashStores = () => { featureTypeStore: new FakeFeatureTypeStore(), resetTokenStore: new FakeResetTokenStore(), sessionStore: new FakeSessionStore(), + userSplashStore: new FakeUserSplashStore(), }; }; From cc516618a1ff1021817033232307bf352e414341 Mon Sep 17 00:00:00 2001 From: Youssef Date: Tue, 9 Nov 2021 14:13:30 +0100 Subject: [PATCH 03/13] add splash-controller and cascade migration --- .../admin-api/user-splash-controller.ts | 76 +++++++++++++++++++ ...11109123505-add-cascade-for-user-splash.js | 16 ++++ 2 files changed, 92 insertions(+) create mode 100644 src/lib/routes/admin-api/user-splash-controller.ts create mode 100644 src/migrations/20211109123505-add-cascade-for-user-splash.js diff --git a/src/lib/routes/admin-api/user-splash-controller.ts b/src/lib/routes/admin-api/user-splash-controller.ts new file mode 100644 index 0000000000..c2e9ad7b14 --- /dev/null +++ b/src/lib/routes/admin-api/user-splash-controller.ts @@ -0,0 +1,76 @@ +import { Response } from 'express'; + +import Controller from '../controller'; +import { Logger } from '../../logger'; +import { IUnleashConfig } from '../../types/option'; +import { IUnleashServices } from '../../types/services'; +import UserSplashService from '../../services/user-splash-service'; +import { IAuthRequest } from '../unleash-types'; + +interface ISplashBody { + seen: boolean; + splashId: string; +} + +class UserSplashController extends Controller { + private logger: Logger; + private userSplashService: UserSplashService; + + constructor( + config: IUnleashConfig, + { userSplashService }: Pick, + ) { + super(config); + this.logger = config.getLogger('splash-controller.ts'); + this.userSplashService = userSplashService; + + this.post('/', this.recordSplash); + this.put('/:id', this.updateSplashSettings); + } + + private async recordSplash( + req: IAuthRequest, + res: Response, + ): Promise { + const BAD_REQUEST = 400; + const { user } = req; + + const { splashId } = req.body; + + if (!splashId) { + res.status(BAD_REQUEST).json({ + error: 'splashId must be present.', + }); + return; + } + + const splash = { + ...req.body, + userId: user.id, + seen: req.body.seen || false, + }; + + const updated = await this.userSplashService.updateSplash(splash); + res.json(updated); + } + + private async updateSplashSettings( + req: IAuthRequest, + res: Response, + ): Promise { + const { user } = req; + const { id } = req.params; + + const splash = { + ...req.body, + splashId: id, + userId: user.id, + seen: req.body.seen || false, + }; + + const updated = await this.userSplashService.updateSplash(splash); + res.json(updated); + } +} + +module.exports = UserSplashController; diff --git a/src/migrations/20211109123505-add-cascade-for-user-splash.js b/src/migrations/20211109123505-add-cascade-for-user-splash.js new file mode 100644 index 0000000000..9a62b4e4e8 --- /dev/null +++ b/src/migrations/20211109123505-add-cascade-for-user-splash.js @@ -0,0 +1,16 @@ +exports.up = function (db, cb) { + db.runSql( + ` + ALTER TABLE user_splash DROP CONSTRAINT user_splash_user_id_fkey; + ALTER TABLE user_splash + ADD CONSTRAINT user_splash_user_id_fkey + FOREIGN KEY (user_id) + REFERENCES users(id) ON DELETE CASCADE; +`, + cb, + ); +}; + +exports.down = function (db, cb) { + db.runSql('', cb); +}; From 96f2514fc5619d7d33105c1c3b44e72095e3cf96 Mon Sep 17 00:00:00 2001 From: Youssef Date: Tue, 9 Nov 2021 20:39:13 +0100 Subject: [PATCH 04/13] send splash data in the user object --- src/lib/routes/admin-api/index.ts | 5 ++++ .../admin-api/user-splash-controller.ts | 1 + src/lib/routes/admin-api/user.ts | 9 ++++--- src/lib/services/user-splash-service.ts | 13 ++++++++-- src/test/fixtures/fake-user-splash-store.ts | 25 ++++--------------- 5 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/lib/routes/admin-api/index.ts b/src/lib/routes/admin-api/index.ts index 3e0edf6114..f1aa75c7fa 100644 --- a/src/lib/routes/admin-api/index.ts +++ b/src/lib/routes/admin-api/index.ts @@ -21,6 +21,7 @@ import ApiTokenController from './api-token-controller'; import UserAdminController from './user-admin'; import EmailController from './email'; import UserFeedbackController from './user-feedback-controller'; +import UserSplashController from './user-splash-controller'; import ProjectApi from './project'; import { EnvironmentsController } from './environments-controller'; @@ -92,6 +93,10 @@ class AdminApi extends Controller { '/environments', new EnvironmentsController(config, services).router, ); + this.app.use( + '/splash', + new UserSplashController(config, services).router, + ); } index(req, res) { diff --git a/src/lib/routes/admin-api/user-splash-controller.ts b/src/lib/routes/admin-api/user-splash-controller.ts index c2e9ad7b14..87815ebd3a 100644 --- a/src/lib/routes/admin-api/user-splash-controller.ts +++ b/src/lib/routes/admin-api/user-splash-controller.ts @@ -74,3 +74,4 @@ class UserSplashController extends Controller { } module.exports = UserSplashController; +export default UserSplashController; \ No newline at end of file diff --git a/src/lib/routes/admin-api/user.ts b/src/lib/routes/admin-api/user.ts index 1d2815a9c6..3e13b4db19 100644 --- a/src/lib/routes/admin-api/user.ts +++ b/src/lib/routes/admin-api/user.ts @@ -7,7 +7,7 @@ import { IUnleashServices } from '../../types/services'; import UserService from '../../services/user-service'; import SessionService from '../../services/session-service'; import UserFeedbackService from '../../services/user-feedback-service'; -import UserSplashService from 'lib/services/user-splash-service'; +import UserSplashService from '../../services/user-splash-service'; interface IChangeUserRequest { password: string; @@ -47,6 +47,7 @@ class UserController extends Controller { this.userService = userService; this.sessionService = sessionService; this.userFeedbackService = userFeedbackService; + this.userSplashService = userSplashService; this.get('/', this.getUser); this.post('/change-password', this.updateUserPass); @@ -62,12 +63,14 @@ class UserController extends Controller { const feedback = await this.userFeedbackService.getAllUserFeedback( user, ); - //const splash = await this.userSplashService.getAllUserSplash(user); + const splash = await this.userSplashService.getAllUserSplashs( + user, + ); // TODO: remove this line after we remove it from db. delete user.permissions; - return res.status(200).json({ user, permissions, feedback }).end(); + return res.status(200).json({ user, permissions, feedback, splash }).end(); } async updateUserPass( diff --git a/src/lib/services/user-splash-service.ts b/src/lib/services/user-splash-service.ts index 9dfb163dd2..65d604ef85 100644 --- a/src/lib/services/user-splash-service.ts +++ b/src/lib/services/user-splash-service.ts @@ -20,12 +20,21 @@ export default class UserSplashService { this.logger = getLogger('services/user-splash-service.js'); } - async getAllUserSplash(user: User): Promise { + async getAllUserSplashs(user: User): Promise { if (user.isAPI) { return []; } try { - return await this.userSplashStore.getAllUserSplashs(user.id); + const splashs = await ( + await this.userSplashStore.getAllUserSplashs(user.id) + ).reduce( + (splashObject, splash) => ({ + ...splashObject, + [splash.splashId]: splash.seen, + }), + {}, + ); + return splashs; } catch (err) { this.logger.error(err); diff --git a/src/test/fixtures/fake-user-splash-store.ts b/src/test/fixtures/fake-user-splash-store.ts index 2fb6af87b8..adc0775c7b 100644 --- a/src/test/fixtures/fake-user-splash-store.ts +++ b/src/test/fixtures/fake-user-splash-store.ts @@ -2,7 +2,7 @@ import { IUserSplashKey, IUserSplash, IUserSplashStore, -} from 'lib/types/stores/user-splash-store'; +} from '../../lib/types/stores/user-splash-store' export default class FakeUserSplashStore implements IUserSplashStore { // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -11,13 +11,13 @@ export default class FakeUserSplashStore implements IUserSplashStore { } // eslint-disable-next-line @typescript-eslint/no-unused-vars - getSplash(userId: number, splashId: string): Promise { - return Promise.resolve({ seen: false, userId: 123, splashId: 'env' }); + getSplash(userId: number, splashId: string): Promise { + return Promise.resolve(undefined); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - updateSplash(splash: IUserSplash): Promise { - return Promise.resolve({ seen: false, userId: 123, splashId: 'env' }); + updateSplash(splash: IUserSplash): Promise { + return Promise.resolve(undefined); } // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -35,21 +35,6 @@ export default class FakeUserSplashStore implements IUserSplashStore { return Promise.resolve([]); } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - getAllUserFeedback(userId: number): Promise { - return Promise.resolve([]); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - getFeedback(userId: number, feedbackId: string): Promise { - return Promise.resolve(undefined); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - updateFeedback(feedback: IUserSplash): Promise { - return Promise.resolve(undefined); - } - // eslint-disable-next-line @typescript-eslint/no-unused-vars delete(key: IUserSplashKey): Promise { return Promise.resolve(undefined); From c4da3e89c5a02364e77e66dbe635a501bdb25e9a Mon Sep 17 00:00:00 2001 From: Youssef Date: Tue, 9 Nov 2021 20:55:23 +0100 Subject: [PATCH 05/13] fix: styling --- src/lib/routes/admin-api/user-splash-controller.ts | 3 ++- src/lib/routes/admin-api/user.ts | 9 +++++---- src/lib/types/stores.ts | 1 - .../20211109123505-add-cascade-for-user-splash.js | 10 +++++----- src/test/fixtures/fake-user-splash-store.ts | 6 +++--- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/lib/routes/admin-api/user-splash-controller.ts b/src/lib/routes/admin-api/user-splash-controller.ts index 87815ebd3a..68872fad54 100644 --- a/src/lib/routes/admin-api/user-splash-controller.ts +++ b/src/lib/routes/admin-api/user-splash-controller.ts @@ -14,6 +14,7 @@ interface ISplashBody { class UserSplashController extends Controller { private logger: Logger; + private userSplashService: UserSplashService; constructor( @@ -74,4 +75,4 @@ class UserSplashController extends Controller { } module.exports = UserSplashController; -export default UserSplashController; \ No newline at end of file +export default UserSplashController; diff --git a/src/lib/routes/admin-api/user.ts b/src/lib/routes/admin-api/user.ts index 3e13b4db19..a0449d1b8d 100644 --- a/src/lib/routes/admin-api/user.ts +++ b/src/lib/routes/admin-api/user.ts @@ -63,14 +63,15 @@ class UserController extends Controller { const feedback = await this.userFeedbackService.getAllUserFeedback( user, ); - const splash = await this.userSplashService.getAllUserSplashs( - user, - ); + const splash = await this.userSplashService.getAllUserSplashs(user); // TODO: remove this line after we remove it from db. delete user.permissions; - return res.status(200).json({ user, permissions, feedback, splash }).end(); + return res + .status(200) + .json({ user, permissions, feedback, splash }) + .end(); } async updateUserPass( diff --git a/src/lib/types/stores.ts b/src/lib/types/stores.ts index ff09b606a5..cb92c0c44a 100644 --- a/src/lib/types/stores.ts +++ b/src/lib/types/stores.ts @@ -25,7 +25,6 @@ import { IFeatureToggleClientStore } from './stores/feature-toggle-client-store' import { IClientMetricsStoreV2 } from './stores/client-metrics-store-v2'; import { IUserSplashStore } from './stores/user-splash-store'; - export interface IUnleashStores { accessStore: IAccessStore; addonStore: IAddonStore; diff --git a/src/migrations/20211109123505-add-cascade-for-user-splash.js b/src/migrations/20211109123505-add-cascade-for-user-splash.js index 9a62b4e4e8..5f9754c440 100644 --- a/src/migrations/20211109123505-add-cascade-for-user-splash.js +++ b/src/migrations/20211109123505-add-cascade-for-user-splash.js @@ -1,16 +1,16 @@ exports.up = function (db, cb) { - db.runSql( - ` + db.runSql( + ` ALTER TABLE user_splash DROP CONSTRAINT user_splash_user_id_fkey; ALTER TABLE user_splash ADD CONSTRAINT user_splash_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; `, - cb, - ); + cb, + ); }; exports.down = function (db, cb) { - db.runSql('', cb); + db.runSql('', cb); }; diff --git a/src/test/fixtures/fake-user-splash-store.ts b/src/test/fixtures/fake-user-splash-store.ts index adc0775c7b..5b3c6e8331 100644 --- a/src/test/fixtures/fake-user-splash-store.ts +++ b/src/test/fixtures/fake-user-splash-store.ts @@ -2,7 +2,7 @@ import { IUserSplashKey, IUserSplash, IUserSplashStore, -} from '../../lib/types/stores/user-splash-store' +} from '../../lib/types/stores/user-splash-store'; export default class FakeUserSplashStore implements IUserSplashStore { // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -11,12 +11,12 @@ export default class FakeUserSplashStore implements IUserSplashStore { } // eslint-disable-next-line @typescript-eslint/no-unused-vars - getSplash(userId: number, splashId: string): Promise { + getSplash(userId: number, splashId: string): Promise { return Promise.resolve(undefined); } // eslint-disable-next-line @typescript-eslint/no-unused-vars - updateSplash(splash: IUserSplash): Promise { + updateSplash(splash: IUserSplash): Promise { return Promise.resolve(undefined); } From db9722dccc22262da692be7e164d8401c11bd821 Mon Sep 17 00:00:00 2001 From: Youssef Date: Wed, 10 Nov 2021 11:24:01 +0100 Subject: [PATCH 06/13] add splash e2e + api tests --- src/test/e2e/api/admin/splash.e2e.test.ts | 97 +++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/test/e2e/api/admin/splash.e2e.test.ts diff --git a/src/test/e2e/api/admin/splash.e2e.test.ts b/src/test/e2e/api/admin/splash.e2e.test.ts new file mode 100644 index 0000000000..263d7319dd --- /dev/null +++ b/src/test/e2e/api/admin/splash.e2e.test.ts @@ -0,0 +1,97 @@ +import { Application, NextFunction, Request, Response } from 'express'; +import { setupAppWithCustomAuth } from '../../helpers/test-helper'; +import dbInit from '../../helpers/database-init'; +import getLogger from '../../../fixtures/no-logger'; +import { IUnleashConfig } from '../../../../lib/types/option'; +import { IUnleashServices } from '../../../../lib/types/services'; + +let stores; +let db; +let app; + +beforeAll(async () => { + db = await dbInit('splash_api_serial', getLogger); + stores = db.stores; + + const email = 'custom-user@mail.com'; + + const preHook = ( + application: Application, + config: IUnleashConfig, + { userService }: IUnleashServices, + ) => { + application.use( + '/api/admin/', + async (req: Request, res: Response, next: NextFunction) => { + // @ts-ignore + req.user = await userService.loginUserWithoutPassword( + email, + true, + ); + next(); + }, + ); + }; + + app = await setupAppWithCustomAuth(stores, preHook); +}); + +afterAll(async () => { + await app.destroy(); + await db.destroy(); +}); + +test('it creates splash for user', async () => { + expect.assertions(1); + + return app.request + .post('/api/admin/splash') + .send({ splashId: 'environment' }) + .set('Content-Type', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => { + expect(res.body.splashId).toBe('environment'); + }); +}); + +test('it gives 400 when splash is not present', async () => { + expect.assertions(1); + + return app.request + .post('/api/admin/splash') + .send({}) + .set('Content-Type', 'application/json') + .expect('Content-Type', /json/) + .expect(400) + .expect((res) => { + expect(res.body.error).toBeTruthy(); + }); +}); + +test('it updates splash for user', async () => { + expect.assertions(1); + + return app.request + .put('/api/admin/splash/environment') + .send({ seen: true }) + .set('Content-Type', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => { + expect(res.body.seen).toBe(true); + }); +}); + +test('it retrieves splash for user', async () => { + expect.assertions(1); + + return app.request + .get('/api/admin/user') + .set('Content-Type', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => { + expect(res.body.splash).toStrictEqual({ environment: true }); + }); +}); From cd38c5a01dca6a5813332149037fa05add0bd642 Mon Sep 17 00:00:00 2001 From: Youssef Date: Wed, 10 Nov 2021 11:24:44 +0100 Subject: [PATCH 07/13] add e2e splash test --- .../e2e/stores/user-splash-store.e2e.test.ts | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 src/test/e2e/stores/user-splash-store.e2e.test.ts diff --git a/src/test/e2e/stores/user-splash-store.e2e.test.ts b/src/test/e2e/stores/user-splash-store.e2e.test.ts new file mode 100644 index 0000000000..73050f2c0b --- /dev/null +++ b/src/test/e2e/stores/user-splash-store.e2e.test.ts @@ -0,0 +1,82 @@ +import { IUserSplashStore } from 'lib/types/stores/user-splash-store'; +import { IUserStore } from 'lib/types/stores/user-store'; +import dbInit from '../helpers/database-init'; +import getLogger from '../../fixtures/no-logger'; + +let stores; +let db; +let userSplashStore: IUserSplashStore; +let userStore: IUserStore; +let currentUser; + +beforeAll(async () => { + db = await dbInit('user_splash_store', getLogger); + stores = db.stores; + userSplashStore = stores.userSplashStore; + userStore = stores.userStore; + currentUser = await userStore.upsert({ email: 'me.feedback@mail.com' }); +}); + +afterAll(async () => { + await db.destroy(); +}); + +afterEach(async () => { + await userSplashStore.deleteAll(); +}); + +test('should create userSplash', async () => { + await userSplashStore.updateSplash({ + splashId: 'some-id', + userId: currentUser.id, + seen: false, + }); + const userSplashs = await userSplashStore.getAllUserSplashs(currentUser.id); + expect(userSplashs).toHaveLength(1); + expect(userSplashs[0].splashId).toBe('some-id'); +}); + +test('should get userSplash', async () => { + await userSplashStore.updateSplash({ + splashId: 'some-id', + userId: currentUser.id, + seen: false, + }); + const userSplash = await userSplashStore.getSplash( + currentUser.id, + 'some-id', + ); + expect(userSplash.splashId).toBe('some-id'); +}); + +test('should exists', async () => { + await userSplashStore.updateSplash({ + splashId: 'some-id-3', + userId: currentUser.id, + seen: false, + }); + const exists = await userSplashStore.exists({ + userId: currentUser.id, + splashId: 'some-id-3', + }); + expect(exists).toBe(true); +}); + +test('should not exists', async () => { + const exists = await userSplashStore.exists({ + userId: currentUser.id, + splashId: 'some-id-not-here', + }); + expect(exists).toBe(false); +}); + +test('should get all userSplashs', async () => { + await userSplashStore.updateSplash({ + splashId: 'some-id-2', + userId: currentUser.id, + seen: false, + }); + const userSplashs = await userSplashStore.getAll(); + expect(userSplashs).toHaveLength(1); + expect(userSplashs[0].splashId).toBe('some-id-2'); +}); From c4e56aae09da6689cd92744260f1d58d6eddfbfa Mon Sep 17 00:00:00 2001 From: Youssef Date: Fri, 12 Nov 2021 10:49:09 +0100 Subject: [PATCH 08/13] fix: remove record splash and update sql query in add-splash-entry --- .../admin-api/user-splash-controller.ts | 32 ++----------------- src/lib/services/user-splash-service.ts | 2 +- ...211109103930-add-splash-entry-for-users.js | 3 +- 3 files changed, 4 insertions(+), 33 deletions(-) diff --git a/src/lib/routes/admin-api/user-splash-controller.ts b/src/lib/routes/admin-api/user-splash-controller.ts index 68872fad54..8f01b2172e 100644 --- a/src/lib/routes/admin-api/user-splash-controller.ts +++ b/src/lib/routes/admin-api/user-splash-controller.ts @@ -25,34 +25,7 @@ class UserSplashController extends Controller { this.logger = config.getLogger('splash-controller.ts'); this.userSplashService = userSplashService; - this.post('/', this.recordSplash); - this.put('/:id', this.updateSplashSettings); - } - - private async recordSplash( - req: IAuthRequest, - res: Response, - ): Promise { - const BAD_REQUEST = 400; - const { user } = req; - - const { splashId } = req.body; - - if (!splashId) { - res.status(BAD_REQUEST).json({ - error: 'splashId must be present.', - }); - return; - } - - const splash = { - ...req.body, - userId: user.id, - seen: req.body.seen || false, - }; - - const updated = await this.userSplashService.updateSplash(splash); - res.json(updated); + this.post('/:id', this.updateSplashSettings); } private async updateSplashSettings( @@ -66,9 +39,8 @@ class UserSplashController extends Controller { ...req.body, splashId: id, userId: user.id, - seen: req.body.seen || false, + seen: true, }; - const updated = await this.userSplashService.updateSplash(splash); res.json(updated); } diff --git a/src/lib/services/user-splash-service.ts b/src/lib/services/user-splash-service.ts index 65d604ef85..0f609499d5 100644 --- a/src/lib/services/user-splash-service.ts +++ b/src/lib/services/user-splash-service.ts @@ -25,7 +25,7 @@ export default class UserSplashService { return []; } try { - const splashs = await ( + const splashs = ( await this.userSplashStore.getAllUserSplashs(user.id) ).reduce( (splashObject, splash) => ({ diff --git a/src/migrations/20211109103930-add-splash-entry-for-users.js b/src/migrations/20211109103930-add-splash-entry-for-users.js index 8a34421415..455f743e0a 100644 --- a/src/migrations/20211109103930-add-splash-entry-for-users.js +++ b/src/migrations/20211109103930-add-splash-entry-for-users.js @@ -2,8 +2,7 @@ exports.up = function (db, cb) { db.runSql(`SELECT * FROM users`, (err, results) => { results.rows.forEach((user) => { db.runSql( - `INSERT INTO user_splash(splash_id, user_id, seen) VALUES (?, ?, ?)`, - ['environments', user.id, false], + `INSERT INTO user_splash(splash_id, user_id, seen) SELECT 'environment', u.id, false FROM users u`, ); }); cb(); From fc275a6183f6fa88da1b73f0d0c8eea29c69138b Mon Sep 17 00:00:00 2001 From: Youssef Date: Fri, 12 Nov 2021 11:02:12 +0100 Subject: [PATCH 09/13] fix: update sql query in add-splash-entry --- .../20211109103930-add-splash-entry-for-users.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/migrations/20211109103930-add-splash-entry-for-users.js b/src/migrations/20211109103930-add-splash-entry-for-users.js index 455f743e0a..65d70586ea 100644 --- a/src/migrations/20211109103930-add-splash-entry-for-users.js +++ b/src/migrations/20211109103930-add-splash-entry-for-users.js @@ -1,12 +1,8 @@ exports.up = function (db, cb) { - db.runSql(`SELECT * FROM users`, (err, results) => { - results.rows.forEach((user) => { - db.runSql( - `INSERT INTO user_splash(splash_id, user_id, seen) SELECT 'environment', u.id, false FROM users u`, - ); - }); - cb(); - }); + db.runSql( + `INSERT INTO user_splash(splash_id, user_id, seen) SELECT 'environment', u.id, false FROM users u`, + cb, + ); }; exports.down = function (db, cb) { From e0356eff6dde9839060eb276550dd106118f0c9b Mon Sep 17 00:00:00 2001 From: Youssef Date: Fri, 12 Nov 2021 11:18:09 +0100 Subject: [PATCH 10/13] fix: update e2e test for splash --- src/test/e2e/api/admin/splash.e2e.test.ts | 31 +---------------------- 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/src/test/e2e/api/admin/splash.e2e.test.ts b/src/test/e2e/api/admin/splash.e2e.test.ts index 263d7319dd..61d0c6db26 100644 --- a/src/test/e2e/api/admin/splash.e2e.test.ts +++ b/src/test/e2e/api/admin/splash.e2e.test.ts @@ -41,40 +41,11 @@ afterAll(async () => { await db.destroy(); }); -test('it creates splash for user', async () => { - expect.assertions(1); - - return app.request - .post('/api/admin/splash') - .send({ splashId: 'environment' }) - .set('Content-Type', 'application/json') - .expect('Content-Type', /json/) - .expect(200) - .expect((res) => { - expect(res.body.splashId).toBe('environment'); - }); -}); - -test('it gives 400 when splash is not present', async () => { - expect.assertions(1); - - return app.request - .post('/api/admin/splash') - .send({}) - .set('Content-Type', 'application/json') - .expect('Content-Type', /json/) - .expect(400) - .expect((res) => { - expect(res.body.error).toBeTruthy(); - }); -}); - test('it updates splash for user', async () => { expect.assertions(1); return app.request - .put('/api/admin/splash/environment') - .send({ seen: true }) + .post('/api/admin/splash/environment') .set('Content-Type', 'application/json') .expect('Content-Type', /json/) .expect(200) From 0199d1f21154db4b58e1c14105b3605c2a06e76b Mon Sep 17 00:00:00 2001 From: Youssef Date: Fri, 12 Nov 2021 12:44:17 +0100 Subject: [PATCH 11/13] fix: remove req.body from the splash object when update --- src/lib/routes/admin-api/user-splash-controller.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/routes/admin-api/user-splash-controller.ts b/src/lib/routes/admin-api/user-splash-controller.ts index 8f01b2172e..07f3b7a049 100644 --- a/src/lib/routes/admin-api/user-splash-controller.ts +++ b/src/lib/routes/admin-api/user-splash-controller.ts @@ -36,7 +36,6 @@ class UserSplashController extends Controller { const { id } = req.params; const splash = { - ...req.body, splashId: id, userId: user.id, seen: true, From 16429fcf9a91f18291b3839fb838faf91856fb50 Mon Sep 17 00:00:00 2001 From: Youssef Date: Fri, 12 Nov 2021 13:01:56 +0100 Subject: [PATCH 12/13] fix: add cascade query inside create table for splash --- .../20211108130333-create-user-splash-table.js | 5 +++-- ...20211109123505-add-cascade-for-user-splash.js | 16 ---------------- 2 files changed, 3 insertions(+), 18 deletions(-) delete mode 100644 src/migrations/20211109123505-add-cascade-for-user-splash.js diff --git a/src/migrations/20211108130333-create-user-splash-table.js b/src/migrations/20211108130333-create-user-splash-table.js index 21953415b3..38ea54e3d9 100644 --- a/src/migrations/20211108130333-create-user-splash-table.js +++ b/src/migrations/20211108130333-create-user-splash-table.js @@ -4,11 +4,12 @@ exports.up = function (db, cb) { db.runSql( ` CREATE TABLE IF NOT EXISTS user_splash - (user_id INTEGER NOT NULL references users (id), + (user_id INTEGER NOT NULL references users (id) ON DELETE CASCADE, splash_id TEXT, seen BOOLEAN NOT NULL DEFAULT false, PRIMARY KEY (user_id, splash_id)); - CREATE INDEX user_splash_user_id_idx ON user_splash (user_id);`, + CREATE INDEX user_splash_user_id_idx ON user_splash (user_id); + `, cb, ); }; diff --git a/src/migrations/20211109123505-add-cascade-for-user-splash.js b/src/migrations/20211109123505-add-cascade-for-user-splash.js deleted file mode 100644 index 5f9754c440..0000000000 --- a/src/migrations/20211109123505-add-cascade-for-user-splash.js +++ /dev/null @@ -1,16 +0,0 @@ -exports.up = function (db, cb) { - db.runSql( - ` - ALTER TABLE user_splash DROP CONSTRAINT user_splash_user_id_fkey; - ALTER TABLE user_splash - ADD CONSTRAINT user_splash_user_id_fkey - FOREIGN KEY (user_id) - REFERENCES users(id) ON DELETE CASCADE; -`, - cb, - ); -}; - -exports.down = function (db, cb) { - db.runSql('', cb); -}; From 2d03b8c61e94cad41ac5ff6aa14e735c7d14fe56 Mon Sep 17 00:00:00 2001 From: Youssef Date: Fri, 12 Nov 2021 13:19:06 +0100 Subject: [PATCH 13/13] fix: return be object instead of array --- src/lib/services/user-splash-service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/services/user-splash-service.ts b/src/lib/services/user-splash-service.ts index 0f609499d5..7d919c7dce 100644 --- a/src/lib/services/user-splash-service.ts +++ b/src/lib/services/user-splash-service.ts @@ -38,7 +38,7 @@ export default class UserSplashService { } catch (err) { this.logger.error(err); - return []; + return {}; } }