Skip to content

Commit

Permalink
refactor: convert to PluginSystemController
Browse files Browse the repository at this point in the history
  • Loading branch information
hung-cybo committed Apr 24, 2024
1 parent b358df3 commit a38d04c
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 182 deletions.
103 changes: 103 additions & 0 deletions packages/plugin-uploader/src/controllers/ControllerBase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import type { Browser, Page } from "puppeteer";
import puppeteer from "puppeteer";
import chalk from "chalk";
import type { BoundMessage } from "../messages";

const TIMEOUT_MS = 10000;

export interface BasicAuth {
username: string;
password: string;
}

export abstract class ControllerBase {
private _browser?: Browser;
private _page?: Page;

public get browser() {
if (this._browser === undefined) {
throw new Error(
"Browser is not launched yet. Please call launchBrowser() first.",
);
}
return this._browser;
}

public set browser(value) {
this._browser = value;
}

public get page() {
if (this._page === undefined) {
throw new Error(
"Page is not opened yet. Please call openNewPage() first.",
);
}
return this._page;
}

public set page(value) {
this._page = value;
}

public async launchBrowser(
options: {
proxy?: string;
ignoreDefaultArgs?: string[];
} = {},
) {
const args = options.proxy ? [`--proxy-server=${options.proxy}`] : [];
this._browser = await puppeteer.launch({
args,
ignoreDefaultArgs: options.ignoreDefaultArgs,
headless: "shell",
});
}

public async closeBrowser() {
return this.browser.close();
}

public async openNewPage() {
this._page = await this.browser.newPage();
}

protected async login(options: {
baseUrl: string;
userName: string;
password: string;
boundMessage: BoundMessage;
basicAuth?: BasicAuth;
}): Promise<void> {
const { baseUrl, userName, password, boundMessage, basicAuth } = options;
const loginUrl = `${baseUrl}/login?saml=off`;

if (basicAuth) {
await this.page.authenticate(basicAuth);
}

console.log(`Open ${loginUrl}`);
await this.page.goto(loginUrl);
try {
await this.page.waitForSelector(".form-username-slash", {
timeout: TIMEOUT_MS,
});
} catch (e) {
throw chalk.red(boundMessage("Error_cannotOpenLogin"));
}

console.log("Trying to log in...");
await this.page.type(".form-username-slash > input.form-text", userName);
await this.page.type(".form-password-slash > input.form-text", password);
await this.page.click(".login-button");

try {
await this.page.waitForNavigation({
timeout: TIMEOUT_MS,
waitUntil: "domcontentloaded",
});
} catch (e) {
throw chalk.red(boundMessage("Error_failedLogin"));
}
}
}
82 changes: 82 additions & 0 deletions packages/plugin-uploader/src/controllers/PluginSystemController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import type { Lang } from "../lang";
import chalk from "chalk";
import { getBoundMessage } from "../messages";
import { ReactPluginSystemPage } from "../pages/ReactPluginSystemPage";
import type { PluginSystemPageInterface } from "../pages/PluginSystemPageInterface";
import {
IMPORT_BUTTON_SELECTOR,
OldPluginSystemPage,
} from "../pages/OldPluginSystemPage";
import { ControllerBase } from "./ControllerBase";

const DETECT_PAGE_TIMEOUT_MS = 10000;
const NO_PRIVILEGE_STATUS_CODE = "403";

export interface BasicAuth {
username: string;
password: string;
}

export default class PluginSystemController extends ControllerBase {
private ui?: PluginSystemPageInterface;

private async getUI() {
if (this.ui) {
return this.ui;
}

const isReactPage = await this.isReactPage();
this.ui = isReactPage
? new ReactPluginSystemPage()
: new OldPluginSystemPage();

return this.ui;
}

private async isReactPage(): Promise<boolean> {
try {
await this.page.waitForSelector(IMPORT_BUTTON_SELECTOR, {
timeout: DETECT_PAGE_TIMEOUT_MS,
});
return false;
} catch (e) {
return true;
}
}

protected async goToPluginSystemPage(baseUrl: string) {
const pluginSystemPageUri = `${baseUrl}/k/admin/system/plugin/`;
console.log(`Navigate to ${pluginSystemPageUri}`);
return this.page.goto(pluginSystemPageUri);
}

public async readyForUpload(options: {
baseUrl: string;
userName: string;
password: string;
lang: Lang;
basicAuth?: BasicAuth;
}): Promise<void> {
const { baseUrl, userName, password, lang, basicAuth } = options;
const boundMessage = getBoundMessage(lang);

await this.login({
baseUrl,
userName,
password,
boundMessage,
basicAuth,
});

const response = await this.goToPluginSystemPage(baseUrl);
if (!response || response.headers().status === NO_PRIVILEGE_STATUS_CODE) {
throw chalk.red(boundMessage("Error_adminPrivilege"));
}

await (await this.getUI()).readyForImportButton(this.page, boundMessage);
}

public async upload(pluginPath: string, lang: Lang): Promise<void> {
await (await this.getUI()).upload(this.page, pluginPath, lang);
}
}
51 changes: 25 additions & 26 deletions packages/plugin-uploader/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from "fs";
import { getBoundMessage } from "./messages";
import type { BasicAuth } from "./pages/PluginSystemPage";
import { PluginSystemPage } from "./pages/PluginSystemPage";
import type { BasicAuth } from "./controllers/ControllerBase";
import PluginSystemController from "./controllers/PluginSystemController";
import type { Lang } from "./lang";

interface Option {
Expand All @@ -19,27 +19,26 @@ export const run = async (
pluginPath: string,
options: Option,
): Promise<void> => {
const pluginSystemPage = new PluginSystemPage();
let browser = await pluginSystemPage.launchBrowser(
options.proxyServer,
options.puppeteerIgnoreDefaultArgs,
);

const { lang, basicAuth } = options;
const boundMessage = getBoundMessage(lang);

const params = {
browser,
baseUrl,
userName,
password,
lang,
basicAuth,
const browserOptions = {
proxy: options.proxyServer,
ignoreDefaultArgs: options.puppeteerIgnoreDefaultArgs,
};
const pluginSystemController = new PluginSystemController();
await pluginSystemController.launchBrowser(browserOptions);

try {
await pluginSystemPage.openNewPage(browser);
await pluginSystemPage.readyForUpload(params);
await pluginSystemPage.upload(pluginPath, lang);
await pluginSystemController.openNewPage();
await pluginSystemController.readyForUpload({
baseUrl,
userName,
password,
lang,
basicAuth,
});
await pluginSystemController.upload(pluginPath, lang);
if (options.watch) {
let uploading = false;
fs.watch(pluginPath, async () => {
Expand All @@ -48,30 +47,30 @@ export const run = async (
}
try {
uploading = true;
await pluginSystemPage.upload(pluginPath, lang);
await pluginSystemController.upload(pluginPath, lang);
} catch (e) {
console.log(e);
console.log(boundMessage("Error_retry"));
await browser.close();
browser = await pluginSystemPage.launchBrowser(options.proxyServer);
await pluginSystemPage.openNewPage(browser);
await pluginSystemPage.readyForUpload({
await pluginSystemController.closeBrowser();
await pluginSystemController.launchBrowser(browserOptions);
await pluginSystemController.openNewPage();
await pluginSystemController.readyForUpload({
baseUrl,
userName,
password,
lang,
basicAuth,
});
await pluginSystemPage.upload(pluginPath, lang);
await pluginSystemController.upload(pluginPath, lang);
} finally {
uploading = false;
}
});
} else {
await browser.close();
await pluginSystemController.closeBrowser();
}
} catch (e) {
console.error(boundMessage("Error"), e);
await browser.close();
await pluginSystemController.closeBrowser();
}
};
2 changes: 1 addition & 1 deletion packages/plugin-uploader/src/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const messages = {
},
Error_notDisplayImportButton: {
en: "Import Button is not displayed.",
ja: "Import Button is not displayed.",
ja: "「読み込む」ボタンが表示されていません。",
},
Uploaded: {
en: "has been uploaded!",
Expand Down
4 changes: 2 additions & 2 deletions packages/plugin-uploader/src/pages/OldPluginSystemPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { BoundMessage } from "../messages";
import { getBoundMessage } from "../messages";
import chalk from "chalk";
import type { Lang } from "../lang";
import type { PluginSystemPageBase } from "./PluginSystemPageBase";
import type { PluginSystemPageInterface } from "./PluginSystemPageInterface";

const TIMEOUT_MS = 10000;
const UPLOAD_TIMEOUT_MS = 60000;
Expand All @@ -13,7 +13,7 @@ export const IMPORT_BUTTON_SELECTOR =
const IMPORT_PLUGIN_DIALOG_SELECTOR = ".ocean-ui-dialog";
const FILE_SELECTOR = '.plupload > input[type="file"]';

export class OldPluginSystemPage implements PluginSystemPageBase {
export class OldPluginSystemPage implements PluginSystemPageInterface {
public async readyForImportButton(
page: Page,
boundMessage: BoundMessage,
Expand Down
Loading

0 comments on commit a38d04c

Please sign in to comment.