From 422e41e0ff29b0171654eaccf526e5be42c8c460 Mon Sep 17 00:00:00 2001 From: Sudharsan Selvaraj Date: Sat, 13 Apr 2024 20:24:58 +0530 Subject: [PATCH] Decouple oss module from pro module --- src/WebsocketHandler.ts | 37 ---------- src/app/routers/grid.ts | 12 ---- src/data-service/device-service.ts | 10 +-- src/events/after-session-deleted-event.ts | 19 +++++ src/events/before-session-create-event.ts | 19 +++++ .../unexpected-server-shutdown-event.ts | 15 ++++ src/fake-module-loader.ts | 3 + src/interfaces/IDeviceFarmSession.ts | 10 +++ src/interfaces/IExternalModule.ts | 3 + src/modules | 2 +- src/plugin.ts | 69 +++++++------------ web/src/api-service/index.ts | 18 +++-- web/yarn.lock | 28 ++++---- 13 files changed, 128 insertions(+), 117 deletions(-) delete mode 100644 src/WebsocketHandler.ts create mode 100644 src/events/after-session-deleted-event.ts create mode 100644 src/events/before-session-create-event.ts create mode 100644 src/events/unexpected-server-shutdown-event.ts diff --git a/src/WebsocketHandler.ts b/src/WebsocketHandler.ts deleted file mode 100644 index 75bd79fa9..000000000 --- a/src/WebsocketHandler.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { waitForCondition } from 'asyncbox'; -import log from './logger'; -import { getStreamingServer } from './modules/device-control/streaming-server'; -import { getIOSStreamingServer } from './modules/device-control/ios-streaming-server'; -import { IPluginArgs } from './interfaces/IPluginArgs'; -import { ADB } from 'appium-adb'; -export async function waitForWebsocketToBeDeregister(pluginArgs: IPluginArgs, httpServer: any) { - if (!pluginArgs.cloud && pluginArgs.platform.toLowerCase() === 'android') { - await waitForCondition(async () => { - console.log('Waiting for Android websocket handlers to be removed..'); - return !Object.hasOwn(await httpServer.getWebSocketHandlers(), '/android-stream/:udid'); - }); - } else if (!pluginArgs.cloud && pluginArgs.platform.toLowerCase() === 'ios') { - await waitForCondition(async () => { - console.log('Waiting for iOS websocket handlers to be removed..'); - return !Object.hasOwn(await httpServer.getWebSocketHandlers(), '/ios-stream/:udid'); - }); - } else { - await waitForCondition(async () => { - console.log('Waiting for Android and iOS websocket handlers to be removed..'); - return ( - !Object.hasOwn(await httpServer.getWebSocketHandlers(), '/android-stream/:udid') && - !Object.hasOwn(await httpServer.getWebSocketHandlers(), '/ios-stream/:udid') - ); - }); - } -} - -export async function registerAndroidWebSocketHandlers(httpServer: any, adbInstance: ADB) { - log.info('Registering websocket handler for Android Streaming'); - await httpServer.addWebSocketHandler('/android-stream/:udid', getStreamingServer(adbInstance)); -} - -export async function registerIOSWebSocketHandlers(httpServer: any) { - log.info('Registering websocket handler for iOS Streaming'); - await httpServer.addWebSocketHandler('/ios-stream/:udid', getIOSStreamingServer()); -} diff --git a/src/app/routers/grid.ts b/src/app/routers/grid.ts index a5f72aabe..f4029e974 100644 --- a/src/app/routers/grid.ts +++ b/src/app/routers/grid.ts @@ -14,13 +14,6 @@ import { DeviceFarmManager } from '../../device-managers'; import Container from 'typedi'; import { IPluginArgs } from '../../interfaces/IPluginArgs'; import { IDevice } from '../../interfaces/IDevice'; -import { - closeSession, - createDriverSession, - installAndroidStreamingApp, - installApk, - installIOSAppOnRealDevice -} from '../../modules/device-control/DeviceHelper'; import path from 'path'; import multer from 'multer'; @@ -280,11 +273,6 @@ function register(router: Router, pluginArgs: IPluginArgs) { router.get('/node/status', nodeAdbStatusOnThisHost); router.get('/node/:host/status', _.curry(nodeAdbStatusOnOtherHost)(pluginArgs.bindHostOrIp)); - router.post('/installAndroidStreamingApp', installAndroidStreamingApp); - router.post('/installApk', installApk); - router.post('/installiOSWDA', installIOSAppOnRealDevice); - router.post('/appiumSession', createDriverSession); - router.post('/closeSession', closeSession); //router.post('/upload', uploadFile); router.post('/upload', upload.single('file'), function (req: any, res) { console.log('storage location is ', req.hostname + '/' + req.file.path); diff --git a/src/data-service/device-service.ts b/src/data-service/device-service.ts index 7b2f9af6e..d7355cc8c 100644 --- a/src/data-service/device-service.ts +++ b/src/data-service/device-service.ts @@ -5,7 +5,7 @@ import log from '../logger'; import { setUtilizationTime } from '../device-utils'; import semver from 'semver'; import debugLog from '../debugLog'; -import { setDeviceState, setDeviceStateWhenUnplugged } from '../modules/device-control/DeviceHelper'; +// import { setDeviceState, setDeviceStateWhenUnplugged } from '../modules/device-control/DeviceHelper'; export async function removeDevice(devices: { udid: string; host: string }[]) { for await (const device of devices) { @@ -15,7 +15,7 @@ export async function removeDevice(devices: { udid: string; host: string }[]) { .find({ udid: device.udid, host: { $contains: device.host } }) .remove(); } - await setDeviceStateWhenUnplugged(); + //await setDeviceStateWhenUnplugged(); } export async function addNewDevice(devices: IDevice[], host?: string): Promise { @@ -66,9 +66,9 @@ export async function addNewDevice(devices: IDevice[], host?: string): Promise { + private static readonly EVENT_NAME: string = 'after-session-deleted'; + + constructor(options: IAfterSessionDeletedEventOptions) { + super(AfterSessionDeletedEvent.EVENT_NAME, options); + } + + static listener(handler: EventConsumer) { + return new EventListener( + AfterSessionDeletedEvent.EVENT_NAME, + handler, + ); + } +} diff --git a/src/events/before-session-create-event.ts b/src/events/before-session-create-event.ts new file mode 100644 index 000000000..1ff7ea428 --- /dev/null +++ b/src/events/before-session-create-event.ts @@ -0,0 +1,19 @@ +import { IBeforeSessionCreateEventOptions } from '../interfaces/IDeviceFarmSession'; +import { Event } from '../notifier/event'; +import { EventListener } from '../notifier/event-listener'; +import { EventConsumer } from '../types/event'; + +export class BeforeSessionCreatedEvent extends Event { + private static readonly EVENT_NAME: string = 'before-session-create'; + + constructor(session: IBeforeSessionCreateEventOptions) { + super(BeforeSessionCreatedEvent.EVENT_NAME, session); + } + + static listener(handler: EventConsumer) { + return new EventListener( + BeforeSessionCreatedEvent.EVENT_NAME, + handler, + ); + } +} diff --git a/src/events/unexpected-server-shutdown-event.ts b/src/events/unexpected-server-shutdown-event.ts new file mode 100644 index 000000000..3609734c8 --- /dev/null +++ b/src/events/unexpected-server-shutdown-event.ts @@ -0,0 +1,15 @@ +import { Event } from '../notifier/event'; +import { EventListener } from '../notifier/event-listener'; +import { EventConsumer } from '../types/event'; + +export class UnexpectedServerShutdownEvent extends Event { + private static readonly EVENT_NAME: string = 'unexpected-server-shutdown-event'; + + constructor() { + super(UnexpectedServerShutdownEvent.EVENT_NAME, null); + } + + static listener(handler: EventConsumer) { + return new EventListener(UnexpectedServerShutdownEvent.EVENT_NAME, handler); + } +} diff --git a/src/fake-module-loader.ts b/src/fake-module-loader.ts index 4e29e8366..3755249c5 100644 --- a/src/fake-module-loader.ts +++ b/src/fake-module-loader.ts @@ -5,13 +5,16 @@ import { IPluginArgs } from './interfaces/IPluginArgs'; import { EventBus } from './notifier/event-bus'; import { Config } from './types/Config'; import { ServerArgs } from '@appium/types'; +import ADB from 'appium-adb'; export class FakeModuleLoader implements IExternalModuleLoader { async onPluginLoaded( serverArgs: ServerArgs, pluginArgs: IPluginArgs, + httpServer: any, config: Config, bus: EventBus, + adb: ADB, ) { //no action } diff --git a/src/interfaces/IDeviceFarmSession.ts b/src/interfaces/IDeviceFarmSession.ts index f9213da46..7ccc156ed 100644 --- a/src/interfaces/IDeviceFarmSession.ts +++ b/src/interfaces/IDeviceFarmSession.ts @@ -1,5 +1,6 @@ import ADB from 'appium-adb'; import { IDevice } from './IDevice'; +import SessionType from '../enums/SessionType'; export type IDeviceFarmSessionOptions = { sessionId: string; @@ -10,3 +11,12 @@ export type IDeviceFarmSessionOptions = { driver: any; adb: ADB; }; + +export type IBeforeSessionCreateEventOptions = { + device: IDevice; + sessionType: SessionType; +}; + +export type IAfterSessionDeletedEventOptions = { + sessionId: string; +}; diff --git a/src/interfaces/IExternalModule.ts b/src/interfaces/IExternalModule.ts index 331212795..94741f5fa 100644 --- a/src/interfaces/IExternalModule.ts +++ b/src/interfaces/IExternalModule.ts @@ -4,6 +4,7 @@ import { EventBus } from '../notifier/event-bus'; import { Config } from '../types/Config'; import http from 'http'; import { ServerArgs, ServerConfig } from '@appium/types'; +import ADB from 'appium-adb'; export type ExpressMiddleware = (request: Request, response: Response, next: NextFunction) => void; @@ -12,7 +13,9 @@ export interface IExternalModuleLoader { serverArgs: ServerArgs, pluginArgs: IPluginArgs, config: Config, + httpServer: any, bus: EventBus, + adb: ADB, ): Promise; getMiddleWares(): ExpressMiddleware[]; diff --git a/src/modules b/src/modules index d0f0a842c..fe9df8058 160000 --- a/src/modules +++ b/src/modules @@ -1 +1 @@ -Subproject commit d0f0a842c874f110c1d93ff6b6f0a17a98b09838 +Subproject commit fe9df80580bef4f13cf4e36de753f9db4ab3e437 diff --git a/src/plugin.ts b/src/plugin.ts index 16e884a4d..c06069ecc 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -69,14 +69,11 @@ import { SessionCreatedEvent } from './events/session-created-event'; import debugLog from './debugLog'; import http from 'http'; import * as https from 'https'; -import { installStreamingApp } from './modules/device-control/androidStreaming'; -import { - registerAndroidWebSocketHandlers, - registerIOSWebSocketHandlers, - waitForWebsocketToBeDeregister, -} from './WebsocketHandler'; -import { downloadAndroidStreamAPK, streamAndroid } from './modules/device-control/DeviceHelper'; import { DEVICE_CONNECTIONS_FACTORY } from 'appium-xcuitest-driver/build/lib/device-connections-factory'; +import { BeforeSessionCreatedEvent } from './events/before-session-create-event'; +import SessionType from './enums/SessionType'; +import { UnexpectedServerShutdownEvent } from './events/unexpected-server-shutdown-event'; +import { AfterSessionDeletedEvent } from './events/after-session-deleted-event'; const commandsQueueGuard = new AsyncLock(); const DEVICE_MANAGER_LOCK_NAME = 'DeviceManager'; @@ -125,9 +122,7 @@ class DevicePlugin extends BasePlugin { deviceFilter, )} onUnexpectedShutdown from server`, ); - await waitForWebsocketToBeDeregister(this.pluginArgs, DevicePlugin.httpServer); - - await DevicePlugin.registerWebSocket(this.pluginArgs); + await EventBus.fire(new UnexpectedServerShutdownEvent()); } public static async updateServer( expressApp: any, @@ -135,6 +130,7 @@ class DevicePlugin extends BasePlugin { cliArgs: ServerArgs, ): Promise { DevicePlugin.httpServer = httpServer; + // cliArgs are here is not pluginArgs yet as it contains the whole CLI argument for Appium! Different case for our plugin constructor log.debug(`📱 Update server with CLI Args: ${JSON.stringify(cliArgs)}`); externalModule = await loadExternalModules(); @@ -149,7 +145,17 @@ class DevicePlugin extends BasePlugin { } else { pluginArgs = Object.assign({}, DefaultPluginArgs); } - externalModule.onPluginLoaded(cliArgs, pluginArgs, pluginConfig, EventBus); + if (pluginArgs.platform.toLowerCase() === 'android') { + DevicePlugin.adbInstance = ADB.createADB({}); + } + externalModule.onPluginLoaded( + cliArgs, + pluginArgs, + httpServer, + pluginConfig, + EventBus, + DevicePlugin.adbInstance, + ); DevicePlugin.NODE_ID = uuidv4(); log.info('Cli Args: ' + JSON.stringify(cliArgs)); @@ -159,7 +165,6 @@ class DevicePlugin extends BasePlugin { if (pluginArgs.bindHostOrIp === undefined) { pluginArgs.bindHostOrIp = ip.address(); } - await DevicePlugin.registerWebSocket(pluginArgs); log.debug(`📱 Update server with Plugin Args: ${JSON.stringify(pluginArgs)}`); await initializeStorage(); (await ADTDatabase.DeviceModel).removeDataOnly(); @@ -263,19 +268,6 @@ class DevicePlugin extends BasePlugin { ); } - static async registerWebSocket(pluginArgs: IPluginArgs) { - if (pluginArgs.platform.toLowerCase() === 'android') { - DevicePlugin.adbInstance = await ADB.createADB({}); - await registerAndroidWebSocketHandlers(DevicePlugin.httpServer, DevicePlugin.adbInstance); - } else if (pluginArgs.platform.toLowerCase() === 'ios') { - await registerIOSWebSocketHandlers(DevicePlugin.httpServer); - } else { - DevicePlugin.adbInstance = await ADB.createADB({}); - await registerAndroidWebSocketHandlers(DevicePlugin.httpServer, DevicePlugin.adbInstance); - await registerIOSWebSocketHandlers(DevicePlugin.httpServer); - } - } - private static setIncludeSimulatorState(pluginArgs: IPluginArgs, deviceTypes: string) { if (hasCloudArgument(pluginArgs)) { deviceTypes = 'real'; @@ -342,18 +334,14 @@ class DevicePlugin extends BasePlugin { debugLog(`📱${pendingSessionId} --- Forwarded session response: ${JSON.stringify(session)}`); } else { log.debug('📱 Creating session on the same node'); - if ( - this.pluginArgs.platform.toLowerCase() === 'android' && - this.pluginArgs.liveStreaming && - !this.pluginArgs.cloud - ) { - log.info('📱 Live streaming argument is set to true, preparing device for live streaming'); - const destination = await downloadAndroidStreamAPK(); - const adbClient = await ADB.createADB({}); - await installStreamingApp(adbClient, device.udid); - log.info(`Installed ${destination} on device ${device.udid}`); - await streamAndroid(adbClient, { udid: device.udid, state: 'device' }, device.systemPort); - } + const sessionType = device.cloud + ? SessionType.CLOUD + : device.nodeId !== DevicePlugin.NODE_ID + ? SessionType.REMOTE + : SessionType.LOCAL; + + await EventBus.fire(new BeforeSessionCreatedEvent({ device, sessionType: sessionType })); + session = await next(); if (caps.alwaysMatch['df:portForward'] !== undefined && device.realDevice) { log.info(`📱 Forwarding ios port to real device ${device.udid} for manual interaction`); @@ -577,12 +565,7 @@ class DevicePlugin extends BasePlugin { await unblockDeviceMatchingFilter({ session_id: sessionId }); log.info(`📱 Unblocking the device that is blocked for session ${sessionId}`); const res = await next(); - try { - await waitForWebsocketToBeDeregister(this.pluginArgs, DevicePlugin.httpServer); - await DevicePlugin.registerWebSocket(this.pluginArgs); - } catch (err) { - log.info('Socket server not removed within 5000ms. So not registering again'); - } + await EventBus.fire(new AfterSessionDeletedEvent({ sessionId: sessionId })); return res; } } diff --git a/web/src/api-service/index.ts b/web/src/api-service/index.ts index e14221d6a..5a0849b95 100644 --- a/web/src/api-service/index.ts +++ b/web/src/api-service/index.ts @@ -6,22 +6,26 @@ export default class DeviceFarmApiService { } public static androidStreamingAppInstalled(udid: string, systemPort: number) { - return apiClient.makePOSTRequest('/installAndroidStreamingApp', {}, { udid, systemPort }); + return apiClient.makePOSTRequest( + '/dashboard/installAndroidStreamingApp', + {}, + { udid, systemPort }, + ); } public static createSession(udid: string, systemPort: number) { return apiClient.makePOSTRequest( - '/appiumSession', + '/dashboard/appiumSession', {}, { udid, systemPort, origin: window.location.origin }, ); } public static installWDAOnDevice(udid: string) { - return apiClient.makePOSTRequest('/installiOSWDA', {}, { udid }); + return apiClient.makePOSTRequest('/dashboard/installiOSWDA', {}, { udid }); } public static installApk(udid: string, apkPath: string) { - return apiClient.makePOSTRequest('/installApk', {}, { udid, apkPath }); + return apiClient.makePOSTRequest('/dashboard/installApk', {}, { udid, apkPath }); } public static getPendingSessionsCount() { return apiClient.makeGETRequest('/queue/length', {}); @@ -52,7 +56,11 @@ export default class DeviceFarmApiService { } public static closeSession(udid: string) { - return apiClient.makePOSTRequest('/closeSession', {}, { udid, origin: window.location.origin }); + return apiClient.makePOSTRequest( + '/dashboard/closeSession', + {}, + { udid, origin: window.location.origin }, + ); } public static async getDeviceLogs(sessionId: string) { diff --git a/web/yarn.lock b/web/yarn.lock index 6da0b2389..87bbac574 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -1867,20 +1867,20 @@ dependencies: "loose-envify" "^1.1.0" -"regenerator-runtime@^0.14.0": - "integrity" "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - "resolved" "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz" - "version" "0.14.1" - -"resolve-from@^4.0.0": - "integrity" "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" - "resolved" "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" - "version" "4.0.0" - -"resolve@^1.19.0": - "integrity" "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==" - "resolved" "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz" - "version" "1.22.8" +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve@^1.19.0: + version "1.22.8" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== dependencies: "is-core-module" "^2.13.0" "path-parse" "^1.0.7"