diff --git a/package-lock.json b/package-lock.json index 94c2c94..49a50a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "express-rate-limit": "^8.1.0", "jsonwebtoken": "^9.0.2", "mongoose": "^8.8.2", + "multer": "^2.0.2", "openweather-api-node": "^3.1.5", "pm2": "^6.0.10", "rimraf": "^5.0.5", @@ -34,6 +35,7 @@ "devDependencies": { "@types/cookie-parser": "^1.4.9", "@types/cors": "^2.8.17", + "@types/multer": "^2.0.0", "@types/supertest": "^6.0.3", "@vitest/coverage-v8": "^3.2.4", "cross-env": "^7.0.3", @@ -2891,6 +2893,16 @@ "version": "2.1.0", "license": "MIT" }, + "node_modules/@types/multer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.0.0.tgz", + "integrity": "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/node": { "version": "20.19.17", "license": "MIT", @@ -3213,6 +3225,12 @@ "node": ">= 8" } }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, "node_modules/aproba": { "version": "2.1.0", "license": "ISC" @@ -3384,6 +3402,17 @@ "version": "1.1.2", "license": "MIT" }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "license": "MIT", @@ -3547,6 +3576,21 @@ "version": "0.0.1", "license": "MIT" }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, "node_modules/console-control-strings": { "version": "1.1.0", "license": "ISC" @@ -4857,7 +4901,6 @@ }, "node_modules/minimist": { "version": "1.2.8", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4998,6 +5041,79 @@ "version": "2.1.3", "license": "MIT" }, + "node_modules/multer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", + "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", + "object-assign": "^4.1.1", + "type-is": "^1.6.18", + "xtend": "^4.0.2" + }, + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/multer/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/multer/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mute-stream": { "version": "0.0.8", "license": "ISC" @@ -5989,6 +6105,14 @@ "dev": true, "license": "MIT" }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "license": "MIT", @@ -6498,6 +6622,12 @@ "node": ">= 0.6" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, "node_modules/typescript": { "version": "5.9.2", "license": "Apache-2.0", @@ -6951,7 +7081,6 @@ }, "node_modules/xtend": { "version": "4.0.2", - "dev": true, "license": "MIT", "engines": { "node": ">=0.4" diff --git a/package.json b/package.json index 57c0c7a..933c244 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "express-rate-limit": "^8.1.0", "jsonwebtoken": "^9.0.2", "mongoose": "^8.8.2", + "multer": "^2.0.2", "openweather-api-node": "^3.1.5", "pm2": "^6.0.10", "rimraf": "^5.0.5", @@ -41,6 +42,7 @@ "devDependencies": { "@types/cookie-parser": "^1.4.9", "@types/cors": "^2.8.17", + "@types/multer": "^2.0.0", "@types/supertest": "^6.0.3", "@vitest/coverage-v8": "^3.2.4", "cross-env": "^7.0.3", diff --git a/src/types/custom.d.ts b/src/types/custom.d.ts index 425dbf9..0b51d76 100644 --- a/src/types/custom.d.ts +++ b/src/types/custom.d.ts @@ -4,21 +4,6 @@ declare global { declare namespace Express { export interface Request { payload: DecodedToken; - file?: Multer.File; - } - - namespace Multer { - export interface File { - fieldname: string; - originalname: string; - encoding: string; - mimetype: string; - size: number; - destination: string; - filename: string; - path: string; - buffer: Buffer; - } } } } diff --git a/tests/rest/auth.test.ts b/tests/rest/auth.test.ts index 8e40373..c588d2a 100644 --- a/tests/rest/auth.test.ts +++ b/tests/rest/auth.test.ts @@ -4,6 +4,7 @@ import express from "express"; import {RestAuth} from "../../src/rest/auth"; import {JwtAuthenticator} from "../../src/utils/jwtAuthenticator"; import {PasswordUtils} from "../../src/utils/passwordUtils"; +// @ts-ignore import {createMockJwtAuthenticator, createMockUserService, createPublicTestApp} from "../helpers/testSetup"; import crypto from "crypto"; diff --git a/tests/rest/jwtTokenPropertiesExtractor.test.ts b/tests/rest/jwtTokenPropertiesExtractor.test.ts index 7c2d5ca..ad24816 100644 --- a/tests/rest/jwtTokenPropertiesExtractor.test.ts +++ b/tests/rest/jwtTokenPropertiesExtractor.test.ts @@ -2,6 +2,7 @@ import { describe, it, expect } from "vitest"; import request from "supertest"; import { JwtTokenPropertiesExtractor } from "../../src/rest/jwtTokenPropertiesExtractor"; +// @ts-ignore import { createTestApp } from "../helpers/testSetup"; describe("JwtTokenPropertiesExtractor", () => { diff --git a/tests/rest/middleware/authenticateJwt.test.ts b/tests/rest/middleware/authenticateJwt.test.ts index 6c2a300..12f0101 100644 --- a/tests/rest/middleware/authenticateJwt.test.ts +++ b/tests/rest/middleware/authenticateJwt.test.ts @@ -2,6 +2,7 @@ import { describe, it, expect, vi, beforeEach, afterEach, type Mocked } from "vi import { Request, Response, NextFunction } from "express"; import { authenticateJwt } from "../../../src/rest/middleware/authenticateJwt"; +// @ts-ignore import { createMockJwtAuthenticator } from "../../helpers/testSetup"; vi.mock("../../../src/utils/jwtAuthenticator"); @@ -43,6 +44,7 @@ describe("authenticateJwt middleware", () => { _authenticateJwt(req, res, next); expect(mockJwtInstance.verifyToken).toHaveBeenCalledWith("valid-jwt-token"); + // @ts-ignore expect(req.payload).toEqual(mockPayload); expect(next).toHaveBeenCalledOnce(); expect(res.status).not.toHaveBeenCalled(); diff --git a/tests/rest/middleware/isAdmin.test.ts b/tests/rest/middleware/isAdmin.test.ts index adb50d9..15d2023 100644 --- a/tests/rest/middleware/isAdmin.test.ts +++ b/tests/rest/middleware/isAdmin.test.ts @@ -1,6 +1,7 @@ import { describe, it, expect, vi, beforeEach, afterEach, type Mocked } from "vitest"; import { Request, Response, NextFunction } from "express"; import { isAdmin } from "../../../src/rest/middleware/isAdmin"; +// @ts-ignore import { createMockUserService } from "../../helpers/testSetup"; import { notFound } from "../../../src/rest/utils/responses"; @@ -28,6 +29,7 @@ describe("isAdmin middleware", () => { mockedUserService = createMockUserService(); req = { + // @ts-ignore payload: { uuid, username: "username", id: ""} }; diff --git a/tests/rest/restUser.test.ts b/tests/rest/restUser.test.ts index 71ab97f..7504b08 100644 --- a/tests/rest/restUser.test.ts +++ b/tests/rest/restUser.test.ts @@ -2,6 +2,7 @@ import {describe, it, expect, vi, beforeEach, afterEach} from "vitest"; import request from "supertest"; import {RestUser} from "../../src/rest/restUser"; +// @ts-ignore import {createMockUserService, setupTestEnvironment, type TestEnvironment} from "../helpers/testSetup"; vi.mock("../../src/services/db/UserService", () => ({ diff --git a/tests/rest/restWebSocket.test.ts b/tests/rest/restWebSocket.test.ts index 1cb33c1..7cca523 100644 --- a/tests/rest/restWebSocket.test.ts +++ b/tests/rest/restWebSocket.test.ts @@ -3,6 +3,7 @@ import request from "supertest"; import express from "express"; import { RestWebSocket } from "../../src/rest/restWebSocket"; +// @ts-ignore import { createTestApp, createMockWebSocketServer } from "../helpers/testSetup"; vi.mock("../../src/websocket", () => ({ diff --git a/tests/rest/spotifyTokenGenerator.test.ts b/tests/rest/spotifyTokenGenerator.test.ts index 96102fd..9af55b4 100644 --- a/tests/rest/spotifyTokenGenerator.test.ts +++ b/tests/rest/spotifyTokenGenerator.test.ts @@ -3,6 +3,7 @@ import request from "supertest"; import express from "express"; import { SpotifyTokenGenerator } from "../../src/rest/spotifyTokenGenerator"; +// @ts-ignore import { createTestApp, createMockSpotifyTokenService } from "../helpers/testSetup"; vi.mock("../../src/db/services/spotifyTokenService"); diff --git a/tests/server.test.ts b/tests/server.test.ts index 6861dba..1d2160b 100644 --- a/tests/server.test.ts +++ b/tests/server.test.ts @@ -4,11 +4,13 @@ import { Server } from "../src/server"; import { Router, type Request, type Response, type NextFunction } from "express"; // Import Express types import type { Express } from "express"; import { authLimiter } from "../src/rest/middleware/rateLimit"; + import { createMockJwtAuthenticator, createMockSpotifyPollingService, createMockSpotifyTokenService, createMockUserService + // @ts-ignore } from "./helpers/testSetup"; diff --git a/tests/services/spotifyPollingService.test.ts b/tests/services/spotifyPollingService.test.ts index e7e7ce4..a90f826 100644 --- a/tests/services/spotifyPollingService.test.ts +++ b/tests/services/spotifyPollingService.test.ts @@ -6,6 +6,7 @@ import { SpotifyTokenService } from "../../src/services/spotifyTokenService"; import { appEventBus, SPOTIFY_STATE_UPDATED_EVENT } from "../../src/utils/eventBus"; import { SpotifyPollingService } from "../../src/services/spotifyPollingService"; import { IUser } from "../../src/db/models/user"; +// @ts-ignore import { createMockSpotifyApiService, createMockSpotifyTokenService, createMockUserService } from "../helpers/testSetup"; vi.mock("../../src/services/db/UserService"); diff --git a/tests/utils/websocket/websocketCustomEvents/websocketEventUtils.test.ts b/tests/utils/websocket/websocketCustomEvents/websocketEventUtils.test.ts index a8ecc87..92777a5 100644 --- a/tests/utils/websocket/websocketCustomEvents/websocketEventUtils.test.ts +++ b/tests/utils/websocket/websocketCustomEvents/websocketEventUtils.test.ts @@ -4,6 +4,7 @@ import {GetStateEvent} from "../../../../src/utils/websocket/websocketCustomEven import {GetSettingsEvent} from "../../../../src/utils/websocket/websocketCustomEvents/getSettingsEvent"; import {GetSpotifyUpdatesEvent} from "../../../../src/utils/websocket/websocketCustomEvents/getSpotifyUpdatesEvent"; import {SpotifyPollingService} from "../../../../src/services/spotifyPollingService"; +// @ts-ignore import {createMockSpotifyPollingService,} from "../../../helpers/testSetup"; import {StopSpotifyUpdatesEvent} from "../../../../src/utils/websocket/websocketCustomEvents/stopSpotifyUpdatesEvent"; import {GetWeatherUpdatesEvent diff --git a/tests/websocket.test.ts b/tests/websocket.test.ts index 0fee8bd..47a25d0 100644 --- a/tests/websocket.test.ts +++ b/tests/websocket.test.ts @@ -1,15 +1,16 @@ -import { describe, it, expect, vi, beforeEach, type Mocked } from "vitest"; -import { Server } from "http"; -import { WebSocket, Server as WebSocketServer } from "ws"; -import { ExtendedWebSocketServer } from "../src/websocket"; -import { WebsocketServerEventHandler } from "../src/utils/websocket/websocketServerEventHandler"; -import { WebsocketEventHandler } from "../src/utils/websocket/websocketEventHandler"; -import { getEventListeners } from "../src/utils/websocket/websocketCustomEvents/websocketEventUtils"; -import { createMockUserService} from "./helpers/testSetup"; +import {describe, it, expect, vi, beforeEach, type Mocked} from "vitest"; +import {Server} from "http"; +import {WebSocket, Server as WebSocketServer} from "ws"; +import {ExtendedWebSocketServer} from "../src/websocket"; +import {WebsocketServerEventHandler} from "../src/utils/websocket/websocketServerEventHandler"; +import {WebsocketEventHandler} from "../src/utils/websocket/websocketEventHandler"; +import {getEventListeners} from "../src/utils/websocket/websocketCustomEvents/websocketEventUtils"; +// @ts-ignore +import {createMockJwtAuthenticator, createMockUserService} from "./helpers/testSetup"; import {UserService} from "../src/services/db/UserService"; import {SpotifyPollingService} from "../src/services/spotifyPollingService"; -import { USER_UPDATED_EVENT, SPOTIFY_STATE_UPDATED_EVENT } from "../src/utils/eventBus"; -import { WebsocketEventType } from "../src/utils/websocket/websocketCustomEvents/websocketEventType"; +import {USER_UPDATED_EVENT, SPOTIFY_STATE_UPDATED_EVENT} from "../src/utils/eventBus"; +import {WebsocketEventType} from "../src/utils/websocket/websocketCustomEvents/websocketEventType"; import {WeatherPollingService} from "../src/services/weatherPollingService"; let mockWssInstance: Mocked; @@ -31,7 +32,7 @@ vi.mock("../src/utils/eventBus", () => ({ vi.mock("ws", () => ({ Server: vi.fn().mockImplementation(() => mockWssInstance), - WebSocket: { OPEN: 1, CLOSED: 3 }, + WebSocket: {OPEN: 1, CLOSED: 3}, })); vi.mock("../src/utils/verifyClient"); @@ -70,7 +71,7 @@ describe("ExtendedWebSocketServer", () => { mockUserService = createMockUserService(); - extendedWss = new ExtendedWebSocketServer(mockHttpServer, mockUserService, mockSpotifyPollingService, mockWeatherPollingService); + extendedWss = new ExtendedWebSocketServer(mockHttpServer, mockUserService, mockSpotifyPollingService, mockWeatherPollingService, createMockJwtAuthenticator() as any); }); describe("Constructor and Setup", () => { @@ -96,22 +97,22 @@ describe("ExtendedWebSocketServer", () => { describe("broadcast", () => { it("should send a message to all connected clients that are OPEN", () => { - const client1 = { readyState: WebSocket.OPEN, send: vi.fn() }; - const client2 = { readyState: WebSocket.CLOSED, send: vi.fn() }; + const client1 = {readyState: WebSocket.OPEN, send: vi.fn()}; + const client2 = {readyState: WebSocket.CLOSED, send: vi.fn()}; mockWssInstance.clients.add(client1 as any).add(client2 as any); extendedWss.broadcast("hello"); - expect(client1.send).toHaveBeenCalledWith("hello", { binary: false }); + expect(client1.send).toHaveBeenCalledWith("hello", {binary: false}); expect(client2.send).not.toHaveBeenCalled(); }); }); describe("sendMessageToUser", () => { it("should send a message to a specific user by their UUID", () => { - const client1 = { readyState: WebSocket.OPEN, payload: { uuid: "uuid-1" }, send: vi.fn() }; - const client2 = { readyState: WebSocket.OPEN, payload: { uuid: "uuid-2" }, send: vi.fn() }; + const client1 = {readyState: WebSocket.OPEN, payload: {uuid: "uuid-1"}, send: vi.fn()}; + const client2 = {readyState: WebSocket.OPEN, payload: {uuid: "uuid-2"}, send: vi.fn()}; mockWssInstance.clients.add(client1 as any).add(client2 as any); extendedWss.sendMessageToUser("uuid-1", "private"); - expect(client1.send).toHaveBeenCalledWith("private", { binary: false }); + expect(client1.send).toHaveBeenCalledWith("private", {binary: false}); expect(client2.send).not.toHaveBeenCalled(); }); }); @@ -124,7 +125,7 @@ describe("ExtendedWebSocketServer", () => { beforeEach(() => { connectionHandler = vi.mocked(mockServerEventHandler.enableConnectionEvent).mock.calls[0][0]; mockWsClient = { - emit: vi.fn(), on: vi.fn(), user: { lastState: { global: { mode: "idle" } } }, + emit: vi.fn(), on: vi.fn(), user: {lastState: {global: {mode: "idle"}}}, }; mockClientEventHandler = { enableErrorEvent: vi.fn(), @@ -135,7 +136,7 @@ describe("ExtendedWebSocketServer", () => { } as unknown as Mocked; vi.mocked(WebsocketEventHandler).mockImplementation(() => mockClientEventHandler); - vi.mocked(getEventListeners).mockReturnValue([{ event: "custom", handler: vi.fn() } as any]); + vi.mocked(getEventListeners).mockReturnValue([{event: "custom", handler: vi.fn()} as any]); }); it("should create and configure a WebsocketEventHandler for new clients", () => { @@ -161,7 +162,7 @@ describe("ExtendedWebSocketServer", () => { beforeEach(() => { mockClient = { readyState: WebSocket.OPEN, - payload: { uuid: "user-123" }, + payload: {uuid: "user-123"}, send: vi.fn(), emit: vi.fn(), }; @@ -172,7 +173,7 @@ describe("ExtendedWebSocketServer", () => { const userUpdateListener = eventBusListeners.get(USER_UPDATED_EVENT); expect(userUpdateListener).toBeDefined(); - const updatedUserPayload = { uuid: "user-123", name: "Neuer Name" }; + const updatedUserPayload = {uuid: "user-123", name: "Neuer Name"}; userUpdateListener!(updatedUserPayload); @@ -187,8 +188,8 @@ describe("ExtendedWebSocketServer", () => { const spotifyStateListener = eventBusListeners.get(SPOTIFY_STATE_UPDATED_EVENT); expect(spotifyStateListener).toBeDefined(); - const spotifyUpdatePayload = { state: { item: { name: "Neuer Song" } } }; - const eventPayload = { uuid: "user-123", ...spotifyUpdatePayload }; + const spotifyUpdatePayload = {state: {item: {name: "Neuer Song"}}}; + const eventPayload = {uuid: "user-123", ...spotifyUpdatePayload}; spotifyStateListener!(eventPayload); @@ -197,14 +198,14 @@ describe("ExtendedWebSocketServer", () => { type: "SPOTIFY_UPDATE", payload: spotifyUpdatePayload.state, }); - expect(mockClient.send).toHaveBeenCalledWith(expectedMessage, { binary: false }); + expect(mockClient.send).toHaveBeenCalledWith(expectedMessage, {binary: false}); }); it("should not send a message if the target client is not connected", () => { const userUpdateListener = eventBusListeners.get(USER_UPDATED_EVENT); const spotifyStateListener = eventBusListeners.get(SPOTIFY_STATE_UPDATED_EVENT); - const eventPayload = { uuid: "user-unknown", name: "some data" }; + const eventPayload = {uuid: "user-unknown", name: "some data"}; userUpdateListener!(eventPayload);