diff --git a/package.json b/package.json index 67880bbb2..16118eea1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "postcat", - "version": "0.4.0", + "version": "0.4.1", "main": "out/app/electron-main/main.js", "description": "A lightweight, extensible API tool", "homepage": "https://github.com/Postcatlab/postcat.git", diff --git a/src/app/electron-main/main.ts b/src/app/electron-main/main.ts index 893cda992..a328466da 100644 --- a/src/app/electron-main/main.ts +++ b/src/app/electron-main/main.ts @@ -3,6 +3,19 @@ import { app, BrowserWindow, ipcMain } from 'electron'; import Store from 'electron-store'; import { LanguageService } from 'pc/app/electron-main/language.service'; import { MockServer } from 'pc/platform/node/mock-server'; +import { + GET_EXT_TABS, + GET_FEATURE, + GET_MOCK_URL, + GET_MODULE, + GET_MODULES, + GET_SIDEBAR_VIEW, + GET_SIDEBAR_VIEWS, + GET_WEBSOCKET_PORT, + INSTALL_MODULE, + LOGIN_WITH, + UNINSTALL_MODULE +} from 'pc/shared/electron-main/constant'; import portfinder from 'portfinder'; import { UnitWorkerModule } from '../../node/test-server/electron/main'; @@ -195,73 +208,86 @@ try { } }); let loginWindow = null; - // 这里可以封装成类+方法匹配调用,不用多个if else - ['on', 'handle'].forEach(eventName => - ipcMain[eventName]('eo-sync', async (event, arg) => { - let returnValue: any; - if (arg.action === 'getModules') { - returnValue = moduleManager.getModules(); - } else if (arg.action === 'getModule') { - returnValue = moduleManager.getModule(arg.data.id); - } else if (arg.action === 'installModule') { - const data = await moduleManager.installExt(arg.data); - returnValue = Object.assign(data, { modules: moduleManager.getModules() }); - } else if (arg.action === 'uninstallModule') { - const data = await moduleManager.uninstall(arg.data); - returnValue = Object.assign(data, { modules: moduleManager.getModules() }); - } else if (arg.action === 'getExtensionPackage') { - returnValue = await moduleManager.getExtensionPackage(arg.data.feature, arg.data.params); - } else if (arg.action === 'getFeature') { - returnValue = moduleManager.getFeature(arg.data.featureKey); - } else if (arg.action === 'getMockUrl') { - // 获取mock服务地址 - returnValue = mockServer.getMockUrl(); - } else if (arg.action === 'getWebsocketPort') { - // 获取websocket服务端口 - returnValue = websocketPort; - } else if (arg.action === 'getExtTabs') { - returnValue = moduleManager.getExtTabs(arg.data.extName); - } else if (arg.action === 'loginWith') { - // * It is eletron, open a new window for login - if (loginWindow) { - loginWindow.destroy(); - loginWindow = null; - } - loginWindow = new BrowserWindow({ - width: 990, - height: 655, - autoHideMenuBar: true, - webPreferences: { - nodeIntegration: false, - contextIsolation: false, - preload: path.join(__dirname, '../../platform/electron-browser/preload.js') - } - }); - loginWindow.loadURL(arg.data.url); - //* Watch the login result - loginWindow.webContents.on('did-navigate', ($event, url = '') => { - const isError = url.includes('request-errors'); - const isSuccess = url.includes('code='); - if (isError || isSuccess) { - loginWindow?.destroy(); - loginWindow = null; - const querys = new URLSearchParams(url.split('?')?.[1]); - eoBrowserWindow.win.webContents.send('thirdLoginCallback', { - isSuccess: isSuccess, - code: querys?.get('code') - }); - } - }); + const getModules = () => Promise.resolve(moduleManager.getModules()); + + const getModule = arg => Promise.resolve(moduleManager.getModule(arg.data.id)); + + const installModule = async arg => { + const data = await moduleManager.installExt(arg.data); + return Object.assign(data, { modules: moduleManager.getModules() }); + }; + + const uninstallModule = async arg => { + const data = await moduleManager.uninstall(arg.data); + return Object.assign(data, { modules: moduleManager.getModules() }); + }; + + const getFeature = arg => Promise.resolve(moduleManager.getFeature(arg.data.featureKey)); + + const getMockUrl = () => Promise.resolve(mockServer.getMockUrl()); + + const getWebsocketPort = () => Promise.resolve(websocketPort); + + const getExtTabs = arg => Promise.resolve(moduleManager.getExtTabs(arg.data.extName)); + + const loginWith = arg => { + if (loginWindow) { + loginWindow.destroy(); + loginWindow = null; + } + loginWindow = new BrowserWindow({ + width: 990, + height: 655, + autoHideMenuBar: true, + webPreferences: { + nodeIntegration: false, + contextIsolation: false, + preload: path.join(__dirname, '../../platform/electron-browser/preload.js') + } + }); + loginWindow.loadURL(arg.data.url); - returnValue = ''; - } else if (arg.action === 'getSidebarView') { - returnValue = moduleManager.getSidebarView(arg.data.extName); - } else if (arg.action === 'getSidebarViews') { - returnValue = moduleManager.getSidebarViews(); - } else { - returnValue = 'Invalid data'; + //* Watch the login result + loginWindow.webContents.on('did-navigate', ($event, url = '') => { + const isError = url.includes('request-errors'); + const isSuccess = url.includes('code='); + if (isError || isSuccess) { + loginWindow?.destroy(); + loginWindow = null; + const querys = new URLSearchParams(url.split('?')?.[1]); + eoBrowserWindow.win.webContents.send('thirdLoginCallback', { + isSuccess: isSuccess, + code: querys?.get('code') + }); } + }); + + return Promise.resolve(''); + }; + + const getSidebarView = arg => Promise.resolve(moduleManager.getSidebarView(arg.data.extName)); + + const getSidebarViews = () => Promise.resolve(moduleManager.getSidebarViews()); + + const action = { + [GET_MODULES]: getModules, + [GET_MODULE]: getModule, + [INSTALL_MODULE]: installModule, + [UNINSTALL_MODULE]: uninstallModule, + [GET_FEATURE]: getFeature, + [GET_MOCK_URL]: getMockUrl, + [GET_WEBSOCKET_PORT]: getWebsocketPort, + [GET_EXT_TABS]: getExtTabs, + // * It is eletron, open a new window for login + [LOGIN_WITH]: loginWith, + [GET_SIDEBAR_VIEW]: getSidebarView, + [GET_SIDEBAR_VIEWS]: getSidebarViews + }; + + ['on', 'handle'].forEach(eventName => + ipcMain[eventName]('eo-sync', async (event, arg) => { + let returnValue = Object.keys(action).includes(arg.action) ? await action[arg.action](arg) : 'Invalid data'; event.returnValue = returnValue; return returnValue; }) diff --git a/src/browser/package.json b/src/browser/package.json index 865c794da..4b5b22d4d 100644 --- a/src/browser/package.json +++ b/src/browser/package.json @@ -53,6 +53,7 @@ "eo-ng-table": "0.1.14", "eo-ng-tabs": "0.1.11", "eo-ng-tree": "0.1.16", + "gpt3-tokenizer": "1.1.5", "is-xml": "0.1.0", "js-beautify": "1.14.7", "lodash-es": "4.17.21", diff --git a/src/browser/src/app/components/chat-robot/chat-robot-message/chat-robot-message.component.scss b/src/browser/src/app/components/chat-robot/chat-robot-message/chat-robot-message.component.scss index 1fbbd793b..7dd22ce72 100644 --- a/src/browser/src/app/components/chat-robot/chat-robot-message/chat-robot-message.component.scss +++ b/src/browser/src/app/components/chat-robot/chat-robot-message/chat-robot-message.component.scss @@ -5,7 +5,6 @@ .message-content { padding: 1rem; border-radius: 0.5rem; - min-width: fit-content; max-width: 400px; } diff --git a/src/browser/src/app/components/eo-ui/monaco-editor/monaco-editor.component.ts b/src/browser/src/app/components/eo-ui/monaco-editor/monaco-editor.component.ts index 6b6f88537..6169ebad9 100644 --- a/src/browser/src/app/components/eo-ui/monaco-editor/monaco-editor.component.ts +++ b/src/browser/src/app/components/eo-ui/monaco-editor/monaco-editor.component.ts @@ -189,7 +189,6 @@ export class EoMonacoEditorComponent implements AfterViewInit, OnInit, OnChanges if (val === this.$$code) { return; } - let code = val; try { if (typeof val === 'object') { diff --git a/src/browser/src/app/components/extension-select/export-api/export-api.component.ts b/src/browser/src/app/components/extension-select/export-api/export-api.component.ts index e7fafc046..a953eb276 100644 --- a/src/browser/src/app/components/extension-select/export-api/export-api.component.ts +++ b/src/browser/src/app/components/extension-select/export-api/export-api.component.ts @@ -4,6 +4,8 @@ import { ExtensionService } from 'pc/browser/src/app/services/extensions/extensi import { Message, MessageService } from 'pc/browser/src/app/services/message'; import { ApiService } from 'pc/browser/src/app/services/storage/api.service'; import { TraceService } from 'pc/browser/src/app/services/trace.service'; +import { EXPORT_API } from 'pc/browser/src/app/shared/constans/featureName'; +import { ExtensionChange } from 'pc/browser/src/app/shared/decorators'; import { FeatureInfo } from 'pc/browser/src/app/shared/models/extension-manager'; import StorageUtil from 'pc/browser/src/app/shared/utils/storage/storage.utils'; import { StoreService } from 'pc/browser/src/app/store/state.service'; @@ -32,17 +34,11 @@ export class ExportApiComponent implements OnInit { ) {} ngOnInit(): void { this.initData(); - this.messageService - .get() - .pipe(takeUntil(this.destroy$)) - .subscribe((inArg: Message) => { - if (inArg.type === 'extensionsChange') { - this.initData(); - } - }); } - initData = () => { - this.featureMap = this.extensionService.getValidExtensionsByFature('exportAPI'); + @ExtensionChange(EXPORT_API, true) + initData() { + console.log('initData'); + this.featureMap = this.extensionService.getValidExtensionsByFature(EXPORT_API); this.supportList = []; this.featureMap?.forEach((data: FeatureInfo, key: string) => { this.supportList.push({ @@ -55,7 +51,7 @@ export class ExportApiComponent implements OnInit { if (!(this.currentExtension && this.supportList.find(val => val.key === this.currentExtension))) { this.currentExtension = key || ''; } - }; + } submit(callback: () => boolean) { this.export(callback); } diff --git a/src/browser/src/app/components/extension-select/import-api/import-api.component.ts b/src/browser/src/app/components/extension-select/import-api/import-api.component.ts index 013accde4..b06f23056 100644 --- a/src/browser/src/app/components/extension-select/import-api/import-api.component.ts +++ b/src/browser/src/app/components/extension-select/import-api/import-api.component.ts @@ -6,6 +6,8 @@ import { Message, MessageService } from 'pc/browser/src/app/services/message'; import { ApiService } from 'pc/browser/src/app/services/storage/api.service'; import { parseAndCheckCollections, parseAndCheckEnv } from 'pc/browser/src/app/services/storage/db/validate/validate'; import { TraceService } from 'pc/browser/src/app/services/trace.service'; +import { IMPORT_API } from 'pc/browser/src/app/shared/constans/featureName'; +import { ExtensionChange } from 'pc/browser/src/app/shared/decorators'; import { FeatureInfo } from 'pc/browser/src/app/shared/models/extension-manager'; import { StoreService } from 'pc/browser/src/app/store/state.service'; import { Subject } from 'rxjs'; @@ -76,14 +78,12 @@ export class ImportApiComponent implements OnInit { this.messageService .get() .pipe(takeUntil(this.destroy$)) - .subscribe((inArg: Message) => { - if (inArg.type === 'extensionsChange') { - this.initData(); - } - }); + .subscribe((inArg: Message) => {}); } - initData = () => { - this.featureMap = this.extensionService.getValidExtensionsByFature('importAPI'); + + @ExtensionChange(IMPORT_API, true) + initData() { + this.featureMap = this.extensionService.getValidExtensionsByFature(IMPORT_API); this.supportList = []; this.featureMap?.forEach((data: FeatureInfo, key: string) => { this.supportList.push({ @@ -96,7 +96,7 @@ export class ImportApiComponent implements OnInit { if (!(this.currentExtension && this.supportList.find(val => val.key === this.currentExtension))) { this.currentExtension = key || ''; } - }; + } uploadChange(data) { this.uploadData = data; } @@ -116,6 +116,7 @@ export class ImportApiComponent implements OnInit { const [data, err] = module[action](content); console.log('import data', window.structuredClone?.(data)); if (err) { + this.eoMessage.error(err.msg); console.error(err.msg); callback(false); return; diff --git a/src/browser/src/app/components/extension-select/push-api/push-api.component.ts b/src/browser/src/app/components/extension-select/push-api/push-api.component.ts index efd635995..5d8297e37 100644 --- a/src/browser/src/app/components/extension-select/push-api/push-api.component.ts +++ b/src/browser/src/app/components/extension-select/push-api/push-api.component.ts @@ -4,6 +4,8 @@ import { has } from 'lodash-es'; import { ExtensionService } from 'pc/browser/src/app/services/extensions/extension.service'; import { Message, MessageService } from 'pc/browser/src/app/services/message'; import { ApiService } from 'pc/browser/src/app/services/storage/api.service'; +import { PUSH_API } from 'pc/browser/src/app/shared/constans/featureName'; +import { ExtensionChange } from 'pc/browser/src/app/shared/decorators'; import { FeatureInfo } from 'pc/browser/src/app/shared/models/extension-manager'; import { Subject, takeUntil } from 'rxjs'; @@ -31,14 +33,12 @@ export class PushApiComponent implements OnInit { this.messageService .get() .pipe(takeUntil(this.destroy$)) - .subscribe((inArg: Message) => { - if (inArg.type === 'extensionsChange') { - this.initData(); - } - }); + .subscribe((inArg: Message) => {}); } - initData = () => { - this.featureMap = this.extensionService.getValidExtensionsByFature('pushAPI'); + + @ExtensionChange(PUSH_API, true) + initData() { + this.featureMap = this.extensionService.getValidExtensionsByFature(PUSH_API); this.supportList = []; this.featureMap?.forEach((data: FeatureInfo, key: string) => { this.supportList.push({ @@ -46,11 +46,16 @@ export class PushApiComponent implements OnInit { ...data }); }); - { - const { key } = this.supportList?.at(0); + // { + // const { key } = this.supportList?.at(0); + // this.currentExtension = key || ''; + // } + if (!this.supportList.length) return; + const { key } = this.supportList.at(0); + if (!(this.currentExtension && this.supportList.find(val => val.key === this.currentExtension))) { this.currentExtension = key || ''; } - }; + } async submit(callback) { const feature = this.featureMap.get(this.currentExtension); if (!feature) { diff --git a/src/browser/src/app/components/extension-select/select/extension-select.component.ts b/src/browser/src/app/components/extension-select/select/extension-select.component.ts index 6ddd4e774..bee041cef 100644 --- a/src/browser/src/app/components/extension-select/select/extension-select.component.ts +++ b/src/browser/src/app/components/extension-select/select/extension-select.component.ts @@ -48,10 +48,14 @@ export class ExtensionSelectComponent { observer.complete(); return; } - parserJsonFile(file).then((result: { name: string }) => { - this.filename = result.name; - this.uploadChange.emit(result); - observer.complete(); - }); + parserJsonFile(file) + .then((result: { name: string }) => { + this.filename = result.name; + this.uploadChange.emit(result); + observer.complete(); + }) + .catch(err => { + this.message.error(err); + }); }); } diff --git a/src/browser/src/app/components/extension-select/sync-api/sync-api.component.ts b/src/browser/src/app/components/extension-select/sync-api/sync-api.component.ts index aba60863e..eec4be043 100644 --- a/src/browser/src/app/components/extension-select/sync-api/sync-api.component.ts +++ b/src/browser/src/app/components/extension-select/sync-api/sync-api.component.ts @@ -6,6 +6,8 @@ import { Message, MessageService } from 'pc/browser/src/app/services/message'; import { ApiService } from 'pc/browser/src/app/services/storage/api.service'; import { TraceService } from 'pc/browser/src/app/services/trace.service'; import { EoSchemaFormComponent } from 'pc/browser/src/app/shared/components/schema-form/schema-form.component'; +import { PULL_API } from 'pc/browser/src/app/shared/constans/featureName'; +import { ExtensionChange } from 'pc/browser/src/app/shared/decorators'; import { FeatureInfo } from 'pc/browser/src/app/shared/models/extension-manager'; import { EffectService } from 'pc/browser/src/app/store/effect.service'; import { StoreService } from 'pc/browser/src/app/store/state.service'; @@ -52,21 +54,7 @@ export class SyncApiComponent implements OnInit, OnChanges { ngOnInit(): void { this.getSyncSettingList(); this.initData(); - this.messageService - .get() - .pipe(takeUntil(this.destroy$)) - .subscribe((inArg: Message) => { - if (inArg.type === 'extensionsChange') { - this.initData(() => { - if (this.supportList?.length) { - const { key } = this.supportList.at(0); - this.model.__formater = key || ''; - } else { - this.model.__formater = ''; - } - }); - } - }); + this.watchInstalledExtensionsChange(); } ngOnChanges(changes: SimpleChanges) { @@ -76,6 +64,18 @@ export class SyncApiComponent implements OnInit, OnChanges { } } + @ExtensionChange(PULL_API) + watchInstalledExtensionsChange() { + this.initData(() => { + if (this.supportList?.length) { + const { key } = this.supportList.at(0); + this.model.__formater = key || ''; + } else { + this.model.__formater = ''; + } + }); + } + handleValueChanges(val) { if (val.__formater !== this.currentFormater?.pluginId) { this.model.__formater = val.__formater; @@ -104,7 +104,7 @@ export class SyncApiComponent implements OnInit, OnChanges { } initData = debounce((afterInitCallback?) => { - this.featureMap = this.extensionService.getValidExtensionsByFature('pullAPI'); + this.featureMap = this.extensionService.getValidExtensionsByFature(PULL_API); this.supportList = []; this.featureMap?.forEach((data: FeatureInfo, key: string) => { this.supportList.push({ diff --git a/src/browser/src/app/components/nps-mask/component/nps-mask.component.ts b/src/browser/src/app/components/nps-mask/component/nps-mask.component.ts index 0c711e19a..61a3c7d97 100644 --- a/src/browser/src/app/components/nps-mask/component/nps-mask.component.ts +++ b/src/browser/src/app/components/nps-mask/component/nps-mask.component.ts @@ -1,8 +1,8 @@ -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; +import { NpsPositionDirective } from 'pc/browser/src/app/components/nps-mask/nps-mask-postion.directive'; import { APP_CONFIG } from 'pc/browser/src/environments/environment'; import { StoreService } from '../../../store/state.service'; -import { NpsPositionDirective } from '../nps-mask-postion.directive'; @Component({ selector: 'pc-nps-mask', @@ -10,8 +10,8 @@ import { NpsPositionDirective } from '../nps-mask-postion.directive';
`, - styleUrls: ['./nps-mask.component.scss'] - // hostDirectives: [NpsPositionDirective] + styleUrls: ['./nps-mask.component.scss'], + hostDirectives: [NpsPositionDirective] }) export class NpsMaskComponent implements OnInit { /** diff --git a/src/browser/src/app/components/nps-mask/nps-mask-postion.directive.ts b/src/browser/src/app/components/nps-mask/nps-mask-postion.directive.ts index 8f5216a7f..99cf8586e 100644 --- a/src/browser/src/app/components/nps-mask/nps-mask-postion.directive.ts +++ b/src/browser/src/app/components/nps-mask/nps-mask-postion.directive.ts @@ -32,7 +32,7 @@ export class NpsPositionDirective { width: `${rect.width - this.padding}px` }); } - resettipsPostion(rect) { + resetTipsPostion(rect) { const tipsDom = this.el.nativeElement.querySelector('.tips'); this.batchSetPropery(tipsDom, { right: `${24}px`, @@ -43,7 +43,7 @@ export class NpsPositionDirective { resetMaskPostion(dom: HTMLIFrameElement) { const rect = dom.getBoundingClientRect(); this.resetTitlePostion(rect); - this.resettipsPostion(rect); + this.resetTipsPostion(rect); } showMask(iframe: HTMLIFrameElement) { this.showTitle = true; @@ -66,8 +66,8 @@ export class NpsPositionDirective { const callback = (mutationList, observer) => { for (const mutation of mutationList) { if (mutation.type !== 'childList' || !mutation.addedNodes.length) return; - const npsDom = mutation.addedNodes[0]; - if (!npsDom.id.includes('howxmSDK')) return; + const npsDom = mutation.previousSibling || mutation.addedNodes[0]; + if (!npsDom?.id?.includes('howxmSDK')) return; //* Reset status after update body,such as refresh page this.hideMask(); @@ -82,11 +82,10 @@ export class NpsPositionDirective { const npsSlideIn = className.includes('widget_SlideInRightBottom'); const hasSubmit = className.includes('modal-widget_widgetTransition') && e[0].attributeName === 'style'; const hasClose = className.includes('modal-widget_backdropBase') && !className.includes('modal-widget_backdropIn'); - // console.log(step, className, npsSlideIn, hasSubmit, hasClose); + console.log(step, className, npsSlideIn, hasSubmit, hasClose); //* 1. Show mask if (iframe && npsSlideIn && step < 1) { step = 1; - // console.log('showMask'); setTimeout(() => { this.showMask(iframe as HTMLIFrameElement); }, 150); diff --git a/src/browser/src/app/core/services/feature-control/feature-control.service.ts b/src/browser/src/app/core/services/feature-control/feature-control.service.ts index aea4c397b..f9a283b0c 100644 --- a/src/browser/src/app/core/services/feature-control/feature-control.service.ts +++ b/src/browser/src/app/core/services/feature-control/feature-control.service.ts @@ -1,5 +1,7 @@ import { Injectable } from '@angular/core'; import { Message, MessageService } from 'pc/browser/src/app/services/message'; +import { FEATURE_CONTROL } from 'pc/browser/src/app/shared/constans/featureName'; +import { ExtensionChange, ExtensionMessage } from 'pc/browser/src/app/shared/decorators'; import featureJSON from './feature.json'; type configKey = keyof typeof featureJSON; @@ -14,28 +16,26 @@ export class FeatureControlService { init() { this.watchExtensionChange(); } - watchExtensionChange() { - this.message.get().subscribe((inArg: Message) => { - if (inArg.type !== 'extensionsChange') return; - const extension = inArg.data.extension; - if (!extension?.features?.featureControl?.length) return; - let aciton = inArg.data.action; - if (inArg.data.action === 'init') { - aciton = extension.enable ? 'enable' : 'disable'; + @ExtensionChange(FEATURE_CONTROL) + watchExtensionChange(inArg?: ExtensionMessage) { + const extension = inArg.data.extension; + let aciton = inArg.data.action; + if (inArg.data.action === 'init') { + aciton = extension.enable ? 'enable' : 'disable'; + } + switch (aciton) { + case 'install': + case 'enable': { + this.openFearure(extension?.features?.featureControl); + break; } - switch (aciton) { - case 'install': - case 'enable': { - this.openFearure(extension?.features?.featureControl); - break; - } - case 'disable': - case 'uninstall': { - this.closeFeature(extension?.features?.featureControl); - break; - } + case 'disable': + case 'uninstall': { + this.closeFeature(extension?.features?.featureControl); + break; } - }); + } + // }); } openFearure(features) { features.forEach(({ id }) => { diff --git a/src/browser/src/app/core/services/theme/theme.service.ts b/src/browser/src/app/core/services/theme/theme.service.ts index f19520caa..fa09aea5f 100644 --- a/src/browser/src/app/core/services/theme/theme.service.ts +++ b/src/browser/src/app/core/services/theme/theme.service.ts @@ -1,6 +1,8 @@ import { DOCUMENT } from '@angular/common'; import { Inject, Injectable } from '@angular/core'; import { kebabCase } from 'lodash-es'; +import { THEME } from 'pc/browser/src/app/shared/constans/featureName'; +import { ExtensionChange, ExtensionMessage } from 'pc/browser/src/app/shared/decorators'; import { SettingService } from '../../../components/system-setting/settings.service'; import { Message, MessageService } from '../../../services/message'; @@ -193,33 +195,31 @@ export class ThemeService { this.changeTheme(currentTheme); } } - watchInstalledExtensionsChange() { - this.message.get().subscribe((inArg: Message) => { - if (inArg.type !== 'extensionsChange') return; - //Rest newest theme list - this.themes = this.themes.filter(val => !val.isExtension); - this.queryExtensionThemes(); - this.afterAllThemeLoad(); + @ExtensionChange(THEME) + watchInstalledExtensionsChange(inArg?: ExtensionMessage) { + if (!inArg) return; + //Rest newest theme list + this.themes = this.themes.filter(val => !val.isExtension); + this.queryExtensionThemes(); + this.afterAllThemeLoad(); - switch (inArg.data.action) { - case 'enable': - case 'install': { - const name = inArg.data.name; - const extension: ExtensionInfo = inArg.data.installedMap.get(name); - if (!extension?.features?.theme?.length) break; + switch (inArg.data.action) { + case 'enable': + case 'install': { + const name = inArg.data.name; + const extension: ExtensionInfo = inArg.data.installedMap.get(name); - //Change theme after install/enable extension - const themeID = extension.features.theme[0].id; - const id = this.themeExtension.getExtensionID(name, themeID); - const theme = this.themes.find(val => val.id === id); - if (!theme) return; - this.changeTheme(theme); - break; - } - default: { - break; - } + //Change theme after install/enable extension + const themeID = extension.features.theme[0].id; + const id = this.themeExtension.getExtensionID(name, themeID); + const theme = this.themes.find(val => val.id === id); + if (!theme) return; + this.changeTheme(theme); + break; } - }); + default: { + break; + } + } } } diff --git a/src/browser/src/app/layouts/sidebar/sidebar.component.ts b/src/browser/src/app/layouts/sidebar/sidebar.component.ts index d7c5b2d13..40396b179 100644 --- a/src/browser/src/app/layouts/sidebar/sidebar.component.ts +++ b/src/browser/src/app/layouts/sidebar/sidebar.component.ts @@ -3,6 +3,8 @@ import { NavigationEnd, Router } from '@angular/router'; import { autorun } from 'mobx'; import { ExtensionService } from 'pc/browser/src/app/services/extensions/extension.service'; import { Message, MessageService } from 'pc/browser/src/app/services/message'; +import { SIDEBAR_VIEW } from 'pc/browser/src/app/shared/constans/featureName'; +import { ExtensionChange, ExtensionMessage } from 'pc/browser/src/app/shared/decorators'; import { ExtensionInfo } from 'pc/browser/src/app/shared/models/extension-manager'; import { StoreService } from 'pc/browser/src/app/store/state.service'; import { Subscription } from 'rxjs'; @@ -68,20 +70,17 @@ export class SidebarComponent implements OnInit, OnDestroy { sidebarViews?.length && this.getIDFromRoute(); } - watchInstalledExtensionsChange() { - this.messageService.get().subscribe((inArg: Message) => { - if (inArg.type === 'extensionsChange') { - if (!this.sidebar.visible) return; - const installedMap = inArg.data.installedMap; - const extensionIDs = Array.isArray(installedMap) ? installedMap.map(n => n.name) : [...installedMap.keys()]; - this.modules = this.modules.filter(n => n.isOffical || extensionIDs.includes(n.id)); - this.initSidebarViews(); - if (!this.modules.some(val => this.router.url.includes(val.activeRoute))) { - pcConsole.warn('sidebar activeRoute not found, redirect to home'); - this.router.navigate(['/home/workspace/project/api']); - } - } - }); + @ExtensionChange(SIDEBAR_VIEW) + watchInstalledExtensionsChange(inArg?: ExtensionMessage) { + if (!this.sidebar.visible) return; + const installedMap = inArg.data.installedMap; + const extensionIDs = Array.isArray(installedMap) ? installedMap.map(n => n.name) : [...installedMap.keys()]; + this.modules = this.modules.filter(n => n.isOffical || extensionIDs.includes(n.id)); + this.initSidebarViews(); + if (!this.modules.some(val => this.router.url.includes(val.activeRoute))) { + pcConsole.warn('sidebar activeRoute not found, redirect to home'); + this.router.navigate(['/home/workspace/project/api']); + } } watchRouterChange() { diff --git a/src/browser/src/app/pages/components/chatgpt-robot/chatgpt-robot.component.ts b/src/browser/src/app/pages/components/chatgpt-robot/chatgpt-robot.component.ts index 319fbbdf5..62e642451 100644 --- a/src/browser/src/app/pages/components/chatgpt-robot/chatgpt-robot.component.ts +++ b/src/browser/src/app/pages/components/chatgpt-robot/chatgpt-robot.component.ts @@ -3,9 +3,12 @@ import { CommonModule } from '@angular/common'; import { HttpClient } from '@angular/common/http'; import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; import { EoNgButtonModule } from 'eo-ng-button'; +import GPT3Tokenizer from 'gpt3-tokenizer'; import { FeatureControlService } from 'pc/browser/src/app/core/services/feature-control/feature-control.service'; import { Message, MessageService } from 'pc/browser/src/app/services/message'; import { TraceService } from 'pc/browser/src/app/services/trace.service'; +import { FEATURE_CONTROL } from 'pc/browser/src/app/shared/constans/featureName'; +import { ExtensionChange, ExtensionMessage } from 'pc/browser/src/app/shared/decorators'; import { ExtensionInfo } from 'pc/browser/src/app/shared/models/extension-manager'; import StorageUtil from 'pc/browser/src/app/shared/utils/storage/storage.utils'; import { StoreService } from 'pc/browser/src/app/store/state.service'; @@ -81,13 +84,15 @@ export class ChatgptRobotComponent implements OnInit { title = $localize`ChatGPT Robot`; loading = false; MAX_LIMIT = 5; + MAX_TOKEN_LENTH_LIMIT = 4000; + appName = 'Postcat'; nowUsage = StorageUtil.get('cr_usage'); initMessage = { date: new Date(), reply: true, type: 'init', user: { - name: 'Postcat', + name: this.appName, avatar: './assets/images/logo.svg' } }; @@ -118,24 +123,63 @@ export class ChatgptRobotComponent implements OnInit { StorageUtil.set('cr_usage', this.nowUsage); }, 5000); } + private getTextLenth(text: string) { + const tokenizer = new GPT3Tokenizer({ type: 'gpt3' }); // or 'codex' + const encoded: { bpe: number[]; text: string[] } = tokenizer.encode(text); + return encoded.text ? encoded.text.length : text.length; + } + private getMessageLength(message: messageItem[]) { + const text = message.map(val => val.text).join(); + return this.getTextLenth(text); + } + private transferMessage2Body(message: messageItem[]): string[] { + return message.map(val => { + if (val.reply) { + return `assistant: ${val.text}`; + } + return `user: ${val.text}`; + }); + } + /** + * Get message object for send API + * + * @param messageNumber number of send/recieve message, 15 messages are kept by default + * @returns + */ + private getMessage(messageNumber = 15): messageItem[] { + if (messageNumber <= 0) return []; + + //Get message by messageNumber,filter out the error message/official message + const result = this.messages + .filter(val => !val.reply || (val.reply && (!val.text.includes('ChatGPT Error:') || val.user?.name === this.appName))) + .slice(-messageNumber); + + //If last question is too long, we need to split it + if (this.getTextLenth(result.at(-1).text) >= this.MAX_TOKEN_LENTH_LIMIT) { + return [{ ...result.at(-1), text: result.at(-1).text.slice(0, this.MAX_TOKEN_LENTH_LIMIT) }]; + } + + //If all messages(ctx) are too long, we need to split it + if (this.getMessageLength(result) >= this.MAX_TOKEN_LENTH_LIMIT) { + const len = Math.floor(messageNumber / 2); + return this.getMessage(len); + } + return result; + } sendChatGPTMessage($event) { this.loading = true; this.trace.report('send_chatGPT'); this.http .post(`${APP_CONFIG.EXTENSION_URL}/chatGPT`, { - message: this.messages.map(val => { - if (val.reply) { - return `assistant: ${val.text}`; - } - return `user: ${val.text}`; - }) + message: this.transferMessage2Body(this.getMessage()) }) .subscribe({ next: (res: any) => { this.loading = false; if (!res?.result) { + const error = res?.error || res?.msg || 'unknown error'; this.messages.push({ - text: `ChatGPT Error: ${res?.error || res?.msg || 'unknown error'}`, + text: `ChatGPT Error: ${error}`, date: new Date(), reply: true, type: 'text', @@ -192,7 +236,7 @@ export class ChatgptRobotComponent implements OnInit { reply: true, type: 'text', user: { - name: 'Postcat', + name: this.appName, avatar: './assets/images/logo.svg' } }); @@ -200,18 +244,15 @@ export class ChatgptRobotComponent implements OnInit { } this.sendChatGPTMessage($event); } - watchExtensionChange() { - this.message.get().subscribe((inArg: Message) => { - if (inArg.type !== 'extensionsChange') return; - const extension: ExtensionInfo = inArg.data.extension; - if (!extension?.features?.featureControl?.length) return; - switch (inArg.data.action) { - case 'install': - case 'enable': { - this.chat.open(); - break; - } + @ExtensionChange(FEATURE_CONTROL) + watchExtensionChange(inArg?: ExtensionMessage) { + switch (inArg.data.action) { + case 'install': + case 'enable': { + this.chat.open(); + break; } - }); + } + // }); } } diff --git a/src/browser/src/app/pages/workspace/overview/edit/workspace-edit.component.ts b/src/browser/src/app/pages/workspace/overview/edit/workspace-edit.component.ts index e99cf00ca..e7d5c2af1 100644 --- a/src/browser/src/app/pages/workspace/overview/edit/workspace-edit.component.ts +++ b/src/browser/src/app/pages/workspace/overview/edit/workspace-edit.component.ts @@ -122,7 +122,7 @@ export class WorkspaceSettingComponent { this.message.error($localize`Delete failed !`); return; } - this.message.success($localize`Delete success !`); + this.message.success($localize`Delete Succeeded`); await this.effect.updateWorkspaceList(); await this.effect.switchWorkspace(this.store.getLocalWorkspace.workSpaceUuid); } diff --git a/src/browser/src/app/pages/workspace/project/api/api.component.ts b/src/browser/src/app/pages/workspace/project/api/api.component.ts index d9b128c6e..08c3c2347 100644 --- a/src/browser/src/app/pages/workspace/project/api/api.component.ts +++ b/src/browser/src/app/pages/workspace/project/api/api.component.ts @@ -6,6 +6,8 @@ import { WebService } from 'pc/browser/src/app/core/services'; import { ExtensionService } from 'pc/browser/src/app/services/extensions/extension.service'; import { ApiData } from 'pc/browser/src/app/services/storage/index.model'; import { TraceService } from 'pc/browser/src/app/services/trace.service'; +import { API_PREVIEW_TAB } from 'pc/browser/src/app/shared/constans/featureName'; +import { ExtensionChange } from 'pc/browser/src/app/shared/decorators'; import { StoreService } from 'pc/browser/src/app/store/state.service'; import { filter, Subject, takeUntil } from 'rxjs'; @@ -94,22 +96,12 @@ export class ApiComponent implements OnInit, OnDestroy { private trace: TraceService ) { this.initExtensionExtra(); - this.watchInstalledExtensionsChange(); - } - watchInstalledExtensionsChange() { - this.messageService.get().subscribe((inArg: Message) => { - if (inArg.type === 'extensionsChange') { - const name = inArg.data.name; - const extension: ExtensionInfo = inArg.data.installedMap.get(name); - if (!extension?.features?.apiPreviewTab) return; - this.initExtensionExtra(); - } - }); } + @ExtensionChange(API_PREVIEW_TAB, true) async initExtensionExtra() { this.rightExtras = []; if (!this.router.url.includes('home/workspace/project/api/http/detail')) return; - const apiPreviewTab = this.extensionService.getValidExtensionsByFature('apiPreviewTab'); + const apiPreviewTab = this.extensionService.getValidExtensionsByFature(API_PREVIEW_TAB); apiPreviewTab?.forEach(async (value, key) => { const module = await this.extensionService.getExtensionPackage(key); const rightExtra = value.rightExtra?.reduce((prev, curr) => { diff --git a/src/browser/src/app/pages/workspace/project/api/components/api-test-form/api-test-form.component.ts b/src/browser/src/app/pages/workspace/project/api/components/api-test-form/api-test-form.component.ts index 79137f04e..2c729512f 100644 --- a/src/browser/src/app/pages/workspace/project/api/components/api-test-form/api-test-form.component.ts +++ b/src/browser/src/app/pages/workspace/project/api/components/api-test-form/api-test-form.component.ts @@ -9,7 +9,12 @@ import { ApiTableService } from '../../service/api-table.service'; selector: 'pc-api-test-form', template: `
- +
@@ -55,6 +60,9 @@ export class ApiTestFormComponent implements OnInit, OnDestroy { this.destroy$.next(); this.destroy$.complete(); } + changeFn($event) { + this.modelChange.emit($event); + } private initListConf() { const config = this.apiTable.initTestTable({ in: this.module, diff --git a/src/browser/src/app/pages/workspace/project/api/components/authorization-extension-form/authorization-extension-form.component.ts b/src/browser/src/app/pages/workspace/project/api/components/authorization-extension-form/authorization-extension-form.component.ts index 966c9ea5e..3f14e902a 100644 --- a/src/browser/src/app/pages/workspace/project/api/components/authorization-extension-form/authorization-extension-form.component.ts +++ b/src/browser/src/app/pages/workspace/project/api/components/authorization-extension-form/authorization-extension-form.component.ts @@ -7,6 +7,8 @@ import { ExtensionService } from 'pc/browser/src/app/services/extensions/extensi import { Message, MessageService } from 'pc/browser/src/app/services/message'; import { Group } from 'pc/browser/src/app/services/storage/db/models'; import { EoSchemaFormComponent } from 'pc/browser/src/app/shared/components/schema-form/schema-form.component'; +import { AUTH_API } from 'pc/browser/src/app/shared/constans/featureName'; +import { ExtensionChange } from 'pc/browser/src/app/shared/decorators'; import { FeatureInfo } from 'pc/browser/src/app/shared/models/extension-manager'; import { PCTree } from 'pc/browser/src/app/shared/utils/tree/tree.utils'; import { Subject, takeUntil } from 'rxjs'; @@ -127,17 +129,13 @@ export class AuthorizationExtensionFormComponent implements OnChanges { makeObservable(this); this.initExtensions(); this.initAutorun(); + this.watchInstalledExtensionsChange(); + } - this.messageService - .get() - .pipe(takeUntil(this.destroy$)) - .subscribe((inArg: Message) => { - if (inArg.type !== 'extensionsChange') return; - const extension = inArg.data.extension; - if (!extension?.features?.authAPI) return; - this.initExtensions(); - this.updateSchema(this.authType); - }); + @ExtensionChange(AUTH_API) + watchInstalledExtensionsChange() { + this.initExtensions(); + this.updateSchema(this.authType); } init() { diff --git a/src/browser/src/app/pages/workspace/project/api/components/group/tree/api-group-tree.component.ts b/src/browser/src/app/pages/workspace/project/api/components/group/tree/api-group-tree.component.ts index c2a76e48f..561a3d66a 100644 --- a/src/browser/src/app/pages/workspace/project/api/components/group/tree/api-group-tree.component.ts +++ b/src/browser/src/app/pages/workspace/project/api/components/group/tree/api-group-tree.component.ts @@ -38,7 +38,7 @@ export class ApiGroupTreeComponent implements OnInit { /** * Expanded keys of tree. */ - expandKeys: string[] = []; + expandKeys: Array = []; requestMethodMap = requestMethodMap; nzSelectedKeys = []; searchValue = ''; @@ -118,7 +118,7 @@ export class ApiGroupTreeComponent implements OnInit { this.isLoading = false; }); autorun(() => { - this.expandKeys = this.getExpandKeys(); + this.expandKeys = [...this.getExpandKeys(), ...this.store.getExpandList].map(Number); this.apiGroupTree = this.store.getApiGroupTree; waitNextTick().then(() => { this.initSelectKeys(); @@ -191,6 +191,8 @@ export class ApiGroupTreeComponent implements OnInit { } addAPI(group?) { const prefix = this.globalStore.isShare ? 'share' : '/home/workspace/project/api'; + // console.log(group?.key); + // this.expandKeys = [...this.expandKeys, group.key]; this.router.navigate([`${prefix}/http/edit`], { queryParams: { groupId: group?.key, pageID: Date.now() } }); @@ -222,6 +224,7 @@ export class ApiGroupTreeComponent implements OnInit { if (node?.group) { node.group.isExpanded = true; } + this.message.success('Add Group successfully'); } importAPI(type: keyof typeof actionComponent, title) { const modal = this.modalService.create({ diff --git a/src/browser/src/app/pages/workspace/project/api/components/history/eo-history.component.html b/src/browser/src/app/pages/workspace/project/api/components/history/eo-history.component.html index 8047ee234..43b7f7ecb 100644 --- a/src/browser/src/app/pages/workspace/project/api/components/history/eo-history.component.html +++ b/src/browser/src/app/pages/workspace/project/api/components/history/eo-history.component.html @@ -7,7 +7,6 @@ nzType="text" [disabled]="!getTestHistory.length" i18n-nzTooltipTitle - nzTooltipTitle="Clear All" eoNgFeedbackTooltip nz-popconfirm i18n-nzPopconfirmTitle diff --git a/src/browser/src/app/pages/workspace/project/api/components/params-import/params-import.component.ts b/src/browser/src/app/pages/workspace/project/api/components/params-import/params-import.component.ts index d010e92af..e88b723f7 100644 --- a/src/browser/src/app/pages/workspace/project/api/components/params-import/params-import.component.ts +++ b/src/browser/src/app/pages/workspace/project/api/components/params-import/params-import.component.ts @@ -81,7 +81,6 @@ export class ParamsImportComponent implements OnInit { } catch (error) {} } else if (['formData', 'header'].includes(this.contentType)) { const arr = form2json(clipText); - console.log(arr); if (Array.isArray(arr) && arr.length && clipText.split(':').length > 1) { this.paramCode = clipText; } diff --git a/src/browser/src/app/pages/workspace/project/api/env/env-select/env-select.component.scss b/src/browser/src/app/pages/workspace/project/api/env/env-select/env-select.component.scss index d1d082955..0381866d1 100644 --- a/src/browser/src/app/pages/workspace/project/api/env/env-select/env-select.component.scss +++ b/src/browser/src/app/pages/workspace/project/api/env/env-select/env-select.component.scss @@ -2,6 +2,28 @@ --select-background-color: var(--background-color); border-bottom: 1px solid var(--border-color); + + ::ng-deep .ant-select .clear-btn:hover eo-iconpark-icon { + background-color: var(--item-active-background-color); + } + + ::ng-deep .ant-select .clear-btn:hover eo-iconpark-icon svg { + color: var(--icon-color) !important; + } + + .clear-btn eo-iconpark-icon { + padding: 3px; + vertical-align: middle; + border-radius: 50%; + transform: translateY(-25%); + } +} + +// TODO: Modify component style + +.ant-tabs-tab-remove { + padding: 0; + margin: 0; } nz-divider { @@ -9,26 +31,22 @@ nz-divider { } :host ::ng-deep { - .env-select-componnet { - .ant-select-clear { - border: 1px solid var(--system-border-color); - border-radius: 50%; - width: 18px; - height: 18px; - display: flex; - align-items: center; - justify-content: center; - margin-top: -8px; - } - } - eo-ng-select eo-ng-select-top-control { border: none !important; } + .ant-select:not(.ant-select-customize-input) .ant-select-selector { + background-color: unset !important; + border: unset !important; + } + .ant-select { width: 140px; } + + .ant-select-selector { + box-shadow: unset !important; + } } .content { diff --git a/src/browser/src/app/pages/workspace/project/api/env/env-select/env-select.component.ts b/src/browser/src/app/pages/workspace/project/api/env/env-select/env-select.component.ts index 6ffd77ee1..eb01e94dd 100644 --- a/src/browser/src/app/pages/workspace/project/api/env/env-select/env-select.component.ts +++ b/src/browser/src/app/pages/workspace/project/api/env/env-select/env-select.component.ts @@ -64,7 +64,7 @@ import { ApiStoreService } from '../../store/api-state.service'; - - + Manage Environment + +
+ +
+
`, styleUrls: ['./env-select.component.scss'] diff --git a/src/browser/src/app/pages/workspace/project/api/http/edit/api-edit-util.service.ts b/src/browser/src/app/pages/workspace/project/api/http/edit/api-edit-util.service.ts index 93b813de1..e64b4da7d 100644 --- a/src/browser/src/app/pages/workspace/project/api/http/edit/api-edit-util.service.ts +++ b/src/browser/src/app/pages/workspace/project/api/http/edit/api-edit-util.service.ts @@ -34,6 +34,7 @@ export class ApiEditUtilService { filterFn: item => { item.partType = mui[tableName]; item.paramType = 0; + delete item['paramAttr.example']; return filterArrFun(item); } }); @@ -46,9 +47,11 @@ export class ApiEditUtilService { ['bodyParams', 'headerParams'].forEach(tableName => { if (tableName === 'bodyParams' && [ApiBodyType.Binary, ApiBodyType.Raw].includes(result.responseList[0].contentType)) { if (result.responseList[0].bodyParams?.[0]) { - result.responseList[0].bodyParams[0].orderNo = 0; - result.responseList[0].bodyParams[0].paramType = 1; - result.responseList[0].bodyParams[0].partType = mui['bodyParams']; + const item = result.responseList[0].bodyParams[0]; + item.orderNo = 0; + item.paramType = 1; + item.partType = mui['bodyParams']; + delete item['paramAttr.example']; } return; } diff --git a/src/browser/src/app/pages/workspace/project/api/http/edit/api-edit.component.ts b/src/browser/src/app/pages/workspace/project/api/http/edit/api-edit.component.ts index 97a07c76e..3b3e89616 100644 --- a/src/browser/src/app/pages/workspace/project/api/http/edit/api-edit.component.ts +++ b/src/browser/src/app/pages/workspace/project/api/http/edit/api-edit.component.ts @@ -170,6 +170,10 @@ export class ApiEditComponent implements OnDestroy, TabViewComponent { } // Add success this.message.success(title); + + if (this.route.snapshot.queryParams.groupId) { + this.store.addApiSuccess(this.route.snapshot.queryParams.groupId); + } busEvent === 'addApi' && this.trace.report('add_api_document_success', { trigger_way: ux, diff --git a/src/browser/src/app/pages/workspace/project/api/http/edit/form/api-edit-form.component.ts b/src/browser/src/app/pages/workspace/project/api/http/edit/form/api-edit-form.component.ts index aa5b29bc7..72efc1ea8 100644 --- a/src/browser/src/app/pages/workspace/project/api/http/edit/form/api-edit-form.component.ts +++ b/src/browser/src/app/pages/workspace/project/api/http/edit/form/api-edit-form.component.ts @@ -42,7 +42,7 @@ export class ApiEditFormComponent implements OnInit { this.initListConf(); } changeFn($event) { - this.modelChange.emit(this.model); + this.modelChange.emit($event); } private initListConf() { const config = this.apiTable.initTable( diff --git a/src/browser/src/app/pages/workspace/project/api/http/test/api-test.component.html b/src/browser/src/app/pages/workspace/project/api/http/test/api-test.component.html index 2a3d709ec..971d2a119 100644 --- a/src/browser/src/app/pages/workspace/project/api/http/test/api-test.component.html +++ b/src/browser/src/app/pages/workspace/project/api/http/test/api-test.component.html @@ -87,10 +87,9 @@ > -
- + +
+ + +
+ +
diff --git a/src/browser/src/app/pages/workspace/project/api/http/test/api-test.component.scss b/src/browser/src/app/pages/workspace/project/api/http/test/api-test.component.scss index 56fc57115..3036e3a1c 100644 --- a/src/browser/src/app/pages/workspace/project/api/http/test/api-test.component.scss +++ b/src/browser/src/app/pages/workspace/project/api/http/test/api-test.component.scss @@ -90,10 +90,6 @@ div.ant-typography { .response_container { @apply h-full overflow-hidden; - - .ant-tabs-tabpane { - min-height: 208px; - } } .top_container { diff --git a/src/browser/src/app/pages/workspace/project/api/http/test/api-test.component.ts b/src/browser/src/app/pages/workspace/project/api/http/test/api-test.component.ts index 578d913f6..eed6cf8de 100644 --- a/src/browser/src/app/pages/workspace/project/api/http/test/api-test.component.ts +++ b/src/browser/src/app/pages/workspace/project/api/http/test/api-test.component.ts @@ -62,7 +62,7 @@ const contentTypeMap = { interface testViewModel { testStartTime?: number; - contentType: ContentType; + monacoContentType: ContentType; autoSetContentType: boolean; requestTabIndex: number; responseTabIndex: number; @@ -110,7 +110,6 @@ export class ApiTestComponent implements OnInit, AfterViewInit, OnDestroy, TabVi REQUEST_METHOD = enumsToArr(RequestMethod); MAX_TEST_SECONDS = 60; isEmpty = isEmpty; - $$contentType: ContentType = contentTypeMap[0]; get uuid() { return this.route.snapshot.queryParams.uuid; @@ -126,12 +125,6 @@ export class ApiTestComponent implements OnInit, AfterViewInit, OnDestroy, TabVi const { uuid } = this.route.snapshot.queryParams; return uuid?.includes?.('history_') ? 'api-test-history' : 'api-test'; } - get contentType(): ContentType { - return contentTypeMap[this.model.request.apiAttrInfo.contentType]; - } - set contentType(value) { - this.$$contentType = value; - } private initTimes = 0; private status$: Subject = new Subject(); @@ -233,7 +226,7 @@ export class ApiTestComponent implements OnInit, AfterViewInit, OnDestroy, TabVi } else { return; } - this.initContentType(); + this.setMonacoContentType(); this.waitSeconds = 0; this.status = 'start'; } else { @@ -253,6 +246,7 @@ export class ApiTestComponent implements OnInit, AfterViewInit, OnDestroy, TabVi if (!this.initialModel) { this.initialModel = eoDeepCopy(this.model); } + this.eoOnInit.emit(this.model); this.cdRef.detectChanges(); } @@ -319,7 +313,7 @@ export class ApiTestComponent implements OnInit, AfterViewInit, OnDestroy, TabVi isFormChange(): boolean { //Has exist api can't save //TODO If has test case,test data will be saved to test case - if (this.model.request.apiUuid) { + if (!this.isEmptyTestPage) { return false; } if (!this.initialModel?.request || !this.model.request) { @@ -340,14 +334,49 @@ export class ApiTestComponent implements OnInit, AfterViewInit, OnDestroy, TabVi return false; } - changeContentType(contentType) { - this.model.request.requestParams.headerParams = this.apiTestUtil.addOrReplaceContentType( - contentType, - this.model.request.requestParams.headerParams - ); + /** + * Return contentType header value by bodyType and monacoContentType + * + * @param bodyType + * @returns + */ + getContentTypeByBodyType(bodyType: ApiBodyType = this.model.request?.apiAttrInfo?.contentType): ContentType | string { + switch (bodyType) { + case ApiBodyType.Raw: { + return this.model?.monacoContentType; + } + case ApiBodyType.FormData: { + return 'multiple/form-data'; + } + case ApiBodyType.Binary: { + return ''; + } + } + } + setHeaderContentType() { + const bodyType = this.model.request?.apiAttrInfo?.contentType; + + if (bodyType !== ApiBodyType.Binary) { + const contentType = this.getContentTypeByBodyType(); + this.model.request.requestParams.headerParams = this.apiTestUtil.addOrReplaceContentType( + contentType, + this.model.request.requestParams.headerParams + ); + return; + } + + //Binary unset request header + const headerIndex = this.model.request.requestParams.headerParams.findIndex(val => val.name.toLowerCase() === 'content-type'); + if (headerIndex === -1) return; + this.model.request.requestParams.headerParams.splice(headerIndex, 1); + + //Angular change value by onPush + this.model.request.requestParams.headerParams = [...this.model.request.requestParams.headerParams]; } changeBodyType($event) { - this.initContentType(); + StorageUtil.set('api_test_body_type', $event); + this.setMonacoContentType(); + this.setHeaderContentType(); } handleBottomTabSelect(tab) { if (tab.index === 2) { @@ -500,11 +529,10 @@ export class ApiTestComponent implements OnInit, AfterViewInit, OnDestroy, TabVi } } } - private initContentType() { + setMonacoContentType($event = this.model.monacoContentType) { const contentType = this.model.request?.apiAttrInfo?.contentType; - if (contentType === ApiBodyType.Raw) { - this.model.contentType = this.apiTestUtil.getContentType(this.model.request.requestParams.headerParams) || 'text/plain'; - } + if (contentType !== ApiBodyType.Raw) return; + this.model.monacoContentType = this.apiTestUtil.getContentType(this.model.request.requestParams.headerParams) || 'text/plain'; } private watchEnvChange() { reaction( @@ -520,9 +548,24 @@ export class ApiTestComponent implements OnInit, AfterViewInit, OnDestroy, TabVi ); } private resetModel() { + const bodyType = typeof StorageUtil.get('api_test_body_type') === 'number' ? StorageUtil.get('api_test_body_type') : ApiBodyType.Raw; + + const headerParams = []; + const contentType = this.getContentTypeByBodyType(bodyType) || contentTypeMap[ApiBodyType.JSON]; + if (bodyType !== ApiBodyType.Binary) { + headerParams.push({ + isRequired: 1, + name: 'content-type', + paramAttr: { + example: contentType + } + }); + } + return { requestTabIndex: 1, responseTabIndex: 0, + monacoContentType: contentType, request: { authInfo: { authInfo: {}, @@ -530,22 +573,14 @@ export class ApiTestComponent implements OnInit, AfterViewInit, OnDestroy, TabVi isInherited: 0 }, apiAttrInfo: { - contentType: ContentTypeEnum.RAW, + contentType: bodyType, requestMethod: 0, beforeInject: '', afterInject: '' }, requestParams: { queryParams: [], - headerParams: [ - { - isRequired: 1, - name: 'content-type', - paramAttr: { - example: CONTENT_TYPE_BY_ABRIDGE[0].value - } - } - ], + headerParams: headerParams, restParams: [], bodyParams: [ { diff --git a/src/browser/src/app/pages/workspace/project/api/http/test/api-test.module.ts b/src/browser/src/app/pages/workspace/project/api/http/test/api-test.module.ts index 8014a96e8..45deee093 100644 --- a/src/browser/src/app/pages/workspace/project/api/http/test/api-test.module.ts +++ b/src/browser/src/app/pages/workspace/project/api/http/test/api-test.module.ts @@ -27,6 +27,7 @@ import { ApiTestBodyComponent } from './body/api-test-body.component'; import { ApiTestResultRequestBodyComponent } from './result-request-body/api-test-result-request-body.component'; import { ApiTestResultResponseComponent } from './result-response/api-test-result-response.component'; import { ByteToStringPipe } from './result-response/get-size.pipe'; +import { TestStatusBarComponent } from './test-status-bar/test-status-bar.component'; const UI_COMPONETS = [ NzTabsModule, @@ -46,7 +47,7 @@ const COMPONENTS = [ ApiScriptComponent ]; @NgModule({ - declarations: [...COMPONENTS, ByteToStringPipe], + declarations: [...COMPONENTS, ByteToStringPipe, TestStatusBarComponent], exports: [...COMPONENTS], imports: [ RouterModule.forChild([ diff --git a/src/browser/src/app/pages/workspace/project/api/http/test/body/api-test-body.component.ts b/src/browser/src/app/pages/workspace/project/api/http/test/body/api-test-body.component.ts index 4e4864ab8..a56f6c488 100644 --- a/src/browser/src/app/pages/workspace/project/api/http/test/body/api-test-body.component.ts +++ b/src/browser/src/app/pages/workspace/project/api/http/test/body/api-test-body.component.ts @@ -73,7 +73,7 @@ export class ApiTestBodyComponent implements OnInit, OnChanges, OnDestroy { private bodyType$: Subject = new Subject(); private destroy$: Subject = new Subject(); get editorType() { - return this.contentType.replace(/.*\//, ''); + return this.contentType?.replace(/.*\//, ''); } constructor(private apiTable: ApiTableService, private message: EoNgFeedbackMessageService) { this.bodyType$.pipe(pairwise(), takeUntil(this.destroy$)).subscribe(val => { diff --git a/src/browser/src/app/pages/workspace/project/api/http/test/result-response/api-test-result-response.component.html b/src/browser/src/app/pages/workspace/project/api/http/test/result-response/api-test-result-response.component.html index 250cb9696..573482bb4 100644 --- a/src/browser/src/app/pages/workspace/project/api/http/test/result-response/api-test-result-response.component.html +++ b/src/browser/src/app/pages/workspace/project/api/http/test/result-response/api-test-result-response.component.html @@ -6,17 +6,6 @@
- -
-
{{ model.statusCode || 'No Response' }}
-
- Size: {{ model.responseLength | byteToString }} - Time: {{ model.time }}ms -
-
+ {{ model.statusCode || 'No Response' }} + Size: {{ model.responseLength | byteToString }} + Time: {{ model.time }} ms +
+ `, + styleUrls: ['./test-status-bar.component.scss'] +}) +export class TestStatusBarComponent implements OnChanges { + @Input() model: ApiTestResData; + codeStatus; + private HTTP_CODE_STATUS = [ + { + cap: 199, + class: 'test-default' + }, + { + status: 'success', + cap: 299, + class: 'test-success' + }, + { + status: 'redirect', + cap: 399, + class: 'test-warning' + }, + { + status: 'clientError', + cap: 499, + class: 'test-error' + }, + { + status: 'serverError', + cap: 599, + class: 'test-error' + } + ]; + ngOnChanges() { + this.codeStatus = this.getHTTPStatus(this.model.statusCode); + } + private getHTTPStatus(statusCode) { + return this.HTTP_CODE_STATUS.find(val => statusCode <= val.cap); + } +} diff --git a/src/browser/src/app/pages/workspace/project/api/service/api-test-util.service.ts b/src/browser/src/app/pages/workspace/project/api/service/api-test-util.service.ts index 16702b78e..364629654 100644 --- a/src/browser/src/app/pages/workspace/project/api/service/api-test-util.service.ts +++ b/src/browser/src/app/pages/workspace/project/api/service/api-test-util.service.ts @@ -14,36 +14,6 @@ import { ApiTestResData } from './test-server/test-server.model'; export class ApiTestUtilService { globalStorageKey = 'EO_TEST_VAR_GLOBALS'; constructor(private apiEditUtil: ApiEditUtilService) {} - getHTTPStatus(statusCode) { - const HTTP_CODE_STATUS = [ - { - status: 'info', - cap: 199, - class: 'test-default' - }, - { - status: 'success', - cap: 299, - class: 'test-success' - }, - { - status: 'redirect', - cap: 399, - class: 'test-warning' - }, - { - status: 'clientError', - cap: 499, - class: 'test-error' - }, - { - status: 'serverError', - cap: 599, - class: 'test-error' - } - ]; - return HTTP_CODE_STATUS.find(val => statusCode <= val.cap); - } /** * Handle api data for judge page has edit * Unlike the saved data, the api data being edited is not as strict @@ -232,7 +202,7 @@ export class ApiTestUtilService { * @param type content-type be added/replaced * @param headers */ - addOrReplaceContentType(contentType: ContentType, headers: HeaderParam[] | any = []) { + addOrReplaceContentType(contentType: ContentType | string, headers: HeaderParam[] | any = []) { const existHeader = headers.find(val => val.name.toLowerCase() === 'content-type'); if (existHeader) { existHeader['paramAttr.example'] = contentType; @@ -242,7 +212,10 @@ export class ApiTestUtilService { { isRequired: 1, name: 'content-type', - 'paramAttr.example': contentType + 'paramAttr.example': contentType, + paramAttr: { + example: contentType + } }, ...headers ]; diff --git a/src/browser/src/app/pages/workspace/project/api/service/test-server/test-server.service.ts b/src/browser/src/app/pages/workspace/project/api/service/test-server/test-server.service.ts index 54a68badd..833a770eb 100644 --- a/src/browser/src/app/pages/workspace/project/api/service/test-server/test-server.service.ts +++ b/src/browser/src/app/pages/workspace/project/api/service/test-server/test-server.service.ts @@ -1,5 +1,5 @@ import { formatDate } from '@angular/common'; -import { Inject, Injectable, LOCALE_ID } from '@angular/core'; +import { Inject, Injectable, Input, LOCALE_ID } from '@angular/core'; import { ApiBodyType, ApiParamsType, JsonRootType, requestMethodMap } from 'pc/browser/src/app/pages/workspace/project/api/api.model'; import { ApiTestUtilService } from 'pc/browser/src/app/pages/workspace/project/api/service/api-test-util.service'; import { @@ -42,10 +42,9 @@ export abstract class TestServerService implements TestServer { .map(val => ({ listDepth: 0, paramKey: val.name, - //@ts-ignore files: val.files?.map(file => file.content), paramType: val.dataType === ApiParamsType.file ? '1' : '0', - paramInfo: val.paramAttr?.example || '' + paramInfo: val.dataType === ApiParamsType.file ? val.files?.map(val => val.name).join(',') : val.paramAttr?.example || '' })); } } diff --git a/src/browser/src/app/pages/workspace/project/api/store/api-state.service.ts b/src/browser/src/app/pages/workspace/project/api/store/api-state.service.ts index dde3fc788..3b08a2774 100644 --- a/src/browser/src/app/pages/workspace/project/api/store/api-state.service.ts +++ b/src/browser/src/app/pages/workspace/project/api/store/api-state.service.ts @@ -12,6 +12,8 @@ export class ApiStoreService { @observable private rootGroup: Group; @observable private groupList: Group[] = []; + @observable private expandList: Array = []; + //? api @observable private apiList = []; @@ -45,6 +47,9 @@ export class ApiStoreService { @computed get getApiList() { return this.apiList; } + @computed get getExpandList() { + return this.expandList; + } @computed get getGroupList() { return this.groupList; } @@ -90,10 +95,18 @@ export class ApiStoreService { this.apiList = list; } + @action addApiSuccess(groupId: string | number) { + this.setExpandsList(groupId); + } + @action setGroupList(list = []) { this.groupList = hangGroupToApi(list); } + @action setExpandsList(expandKey: string | number) { + this.expandList = [...this.expandList, expandKey]; + } + @action setEnvUuid(data) { this.envUuid = data; StorageUtil.set('env:selected', data); diff --git a/src/browser/src/app/pages/workspace/project/setting/project-setting.component.ts b/src/browser/src/app/pages/workspace/project/setting/project-setting.component.ts index ea6b20e71..bede5ca6d 100644 --- a/src/browser/src/app/pages/workspace/project/setting/project-setting.component.ts +++ b/src/browser/src/app/pages/workspace/project/setting/project-setting.component.ts @@ -1,7 +1,7 @@ import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { Router } from '@angular/router'; import { EoNgFeedbackMessageService } from 'eo-ng-feedback'; -import { autorun, toJS } from 'mobx'; +import { autorun, reaction, toJS } from 'mobx'; import { NzModalRef } from 'ng-zorro-antd/modal'; import { ExtensionService } from 'pc/browser/src/app/services/extensions/extension.service'; import { TraceService } from 'pc/browser/src/app/services/trace.service'; @@ -117,10 +117,17 @@ export class ProjectSettingComponent implements OnInit { ]; ngOnInit(): void { - autorun(() => { - this.projectName = this.store.getCurrentProject.name; - this.isInit = true; - }); + this.projectName = this.store.getCurrentProject?.name; + reaction( + () => this.store.getCurrentProject, + project => { + console.log(project); + if (project.name) { + this.projectName = project.name; + } + this.isInit = true; + } + ); } startEditProjectName() { diff --git a/src/browser/src/app/services/extensions/extension.service.ts b/src/browser/src/app/services/extensions/extension.service.ts index fb7e410da..d4cfba3b3 100644 --- a/src/browser/src/app/services/extensions/extension.service.ts +++ b/src/browser/src/app/services/extensions/extension.service.ts @@ -3,6 +3,7 @@ import { Injectable } from '@angular/core'; import { ElectronService } from 'pc/browser/src/app/core/services'; import { LanguageService } from 'pc/browser/src/app/core/services/language/language.service'; import { MessageService } from 'pc/browser/src/app/services/message'; +import { extensionMessageSubject } from 'pc/browser/src/app/shared/decorators'; import { defaultExtensions } from 'pc/browser/src/app/shared/models/extension'; import { FeatureInfo, ExtensionInfo, SidebarView } from 'pc/browser/src/app/shared/models/extension-manager'; import { DISABLE_EXTENSION_NAMES } from 'pc/browser/src/app/shared/models/storageKeys.constant'; @@ -92,8 +93,7 @@ export class ExtensionService { }); this.extensionIDs = this.updateExtensionIDs(); this.installedList = Array.from(this.installedMap.values()).filter(it => this.extensionIDs.includes(it.name)); - this.messageService.send({ - type: 'extensionsChange', + extensionMessageSubject.next({ data: { installedMap: this.installedMap, extension: this.installedList.find(val => val.name === opts.name), diff --git a/src/browser/src/app/services/storage/db/models/apiData.ts b/src/browser/src/app/services/storage/db/models/apiData.ts index 986ab8399..5fc74d3d7 100644 --- a/src/browser/src/app/services/storage/db/models/apiData.ts +++ b/src/browser/src/app/services/storage/db/models/apiData.ts @@ -170,6 +170,8 @@ export interface BodyParam { structureId?: number; structureParamId?: string; isRequired?: number; + //Use in test page + files?: any[]; binaryRawData?: string; description?: string; orderNo?: number; diff --git a/src/browser/src/app/shared/constans/featureName.ts b/src/browser/src/app/shared/constans/featureName.ts new file mode 100644 index 000000000..8b7fbf02d --- /dev/null +++ b/src/browser/src/app/shared/constans/featureName.ts @@ -0,0 +1,9 @@ +export const IMPORT_API = 'importAPI'; +export const API_PREVIEW_TAB = 'apiPreviewTab'; +export const EXPORT_API = 'exportAPI'; +export const PUSH_API = 'pushAPI'; +export const PULL_API = 'pullAPI'; +export const FEATURE_CONTROL = 'featureControl'; +export const THEME = 'theme'; +export const SIDEBAR_VIEW = 'sidebarView'; +export const AUTH_API = 'authAPI'; diff --git a/src/browser/src/app/shared/decorators/index.ts b/src/browser/src/app/shared/decorators/index.ts new file mode 100644 index 000000000..5d335b6e0 --- /dev/null +++ b/src/browser/src/app/shared/decorators/index.ts @@ -0,0 +1,50 @@ +import { ExtensionInfo } from 'pc/browser/src/app/shared/models/extension-manager'; +import { Observable, Subject } from 'rxjs'; + +export interface DataType { + installedMap: Map; + extension: ExtensionInfo; + action: string; + name: string; +} + +export type ExtensionMessage = { + /** + * Message data + * + * @data {DataType} + */ + data: DataType; +}; + +export const extensionMessageSubject = new Subject(); + +const extensionsChangeObserve: Observable = extensionMessageSubject.asObservable(); + +/** + * @description Plug-in manipulates the decorator + * @param {string} feature feature + * @param {boolean} autoRun autoRun + * @return {(Object, string, PropertyDescriptor) => PropertyDescriptor} function + */ +export function ExtensionChange(feature: string, autoRun: boolean = false) { + return function (target: Object, key: string, descriptor: PropertyDescriptor) { + const original = descriptor.value; + descriptor.value = function (...args: any[]) { + if (autoRun) { + // It is recommended to call the outer function directly only once + original.apply(this, args); + } + extensionsChangeObserve.subscribe((inArg: ExtensionMessage) => { + const extension: ExtensionInfo = inArg.data.extension; + if (Object.keys(extension?.features).includes(feature)) { + const result = original.apply(this, [...args, inArg]); + return result; + } else { + return null; + } + }); + }; + return descriptor; + }; +} diff --git a/src/browser/src/app/shared/models/extension.ts b/src/browser/src/app/shared/models/extension.ts index bcb0c5e11..d65237de0 100644 --- a/src/browser/src/app/shared/models/extension.ts +++ b/src/browser/src/app/shared/models/extension.ts @@ -1 +1 @@ -export const defaultExtensions = ['postcat-export-openapi', 'postcat-import-openapi']; +export const defaultExtensions = ['postcat-export-openapi', 'postcat-import-openapi', 'postcat-basic-auth']; diff --git a/src/browser/src/app/shared/shared.module.ts b/src/browser/src/app/shared/shared.module.ts index 57823ac46..2ba704757 100644 --- a/src/browser/src/app/shared/shared.module.ts +++ b/src/browser/src/app/shared/shared.module.ts @@ -18,6 +18,7 @@ import { NzListModule } from 'ng-zorro-antd/list'; import { NzModalModule } from 'ng-zorro-antd/modal'; import { NzPopconfirmModule } from 'ng-zorro-antd/popconfirm'; import { NzPopoverModule } from 'ng-zorro-antd/popover'; +import { NzSelectModule } from 'ng-zorro-antd/select'; import { NzSkeletonModule } from 'ng-zorro-antd/skeleton'; import { NzSpinModule } from 'ng-zorro-antd/spin'; import { NzTypographyModule } from 'ng-zorro-antd/typography'; @@ -62,6 +63,7 @@ const SHARED_UI_MODULE = [ NzEmptyModule, NzModalModule, NzListModule, + NzSelectModule, NzPopconfirmModule ]; const SHARED_MODULE = [CommonModule, FormsModule, RouterModule, ReactiveFormsModule]; diff --git a/src/browser/src/app/shared/utils/data-transfer/data-transfer.utils.ts b/src/browser/src/app/shared/utils/data-transfer/data-transfer.utils.ts index 24c164cc3..09c6052ef 100644 --- a/src/browser/src/app/shared/utils/data-transfer/data-transfer.utils.ts +++ b/src/browser/src/app/shared/utils/data-transfer/data-transfer.utils.ts @@ -69,7 +69,7 @@ export const form2json = tmpl => tmpl .split('\n') .filter(it => it.trim()) - .map(it => it.split(':')) + .map(it => [it.slice(0, it.indexOf(':')), it.slice(it.indexOf(':') + 1)]) .map(it => { const [key, value] = it; return { key: key?.trim(), value: value?.trim() }; diff --git a/src/browser/src/app/shared/utils/index.utils.ts b/src/browser/src/app/shared/utils/index.utils.ts index 3a1fc0428..4dc515c63 100644 --- a/src/browser/src/app/shared/utils/index.utils.ts +++ b/src/browser/src/app/shared/utils/index.utils.ts @@ -111,13 +111,17 @@ export const transferFileToDataUrl = file => }; }); export const parserJsonFile = (file, type = 'UTF-8') => - new Promise(resolve => { + new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsText(file, type); reader.onload = ev => { - const fileString: string = ev.target.result as string; - const json = JSON.parse(fileString); - resolve({ name: file.name, content: json }); + try { + const fileString: string = ev.target.result as string; + const json = JSON.parse(fileString); + resolve({ name: file.name, content: json }); + } catch (err) { + reject('This is not a json'); + } }; }); diff --git a/src/browser/src/app/store/effect.service.ts b/src/browser/src/app/store/effect.service.ts index 5318decdd..9bd29491b 100644 --- a/src/browser/src/app/store/effect.service.ts +++ b/src/browser/src/app/store/effect.service.ts @@ -174,7 +174,8 @@ export class EffectService { } // * update project auth const [data, err]: any = await this.api.api_projectGetRole({}); - if (err) { + if (err || !data.length) { + pcConsole.error('Get Project Role error'); return; } const { permissions, roles } = data.at(0); diff --git a/src/node/test-server/package.json b/src/node/test-server/package.json index c5bdbf85f..63815cf9a 100644 --- a/src/node/test-server/package.json +++ b/src/node/test-server/package.json @@ -8,7 +8,9 @@ "url": "git+git@github.com:Postcatlab/postcat.git" }, "scripts": { - "dev": "node ./server/main.js&&node ./server/socketio.js", + "dev": "npm-run-all -p dev:http dev:ws", + "dev:http":"node ./server/main.js", + "dev:ws":"node ./server/socketio.js", "start": "pm2 start ecosystem.config.js", "start:watch": "pm2-runtime start ecosystem.config.js", "stop": "pm2 stop ecosystem.config.js", diff --git a/src/node/test-server/request/libs/apiUtil.js b/src/node/test-server/request/libs/apiUtil.js index c4782af63..2610debfb 100644 --- a/src/node/test-server/request/libs/apiUtil.js +++ b/src/node/test-server/request/libs/apiUtil.js @@ -419,7 +419,6 @@ privateFun.parseBeforeCode = async function (scritEngines = 'pm', inputData, inp let tmpEnviroments; let tmpBinary = inputData.binary; inputOpts.authInfo = inputOpts.authInfo || { authType: 'none', authInfo: {} }; - switch (scritEngines) { case 'pm': { tmpTargetTypeEnv = { @@ -522,10 +521,9 @@ privateFun.parseBeforeCode = async function (scritEngines = 'pm', inputData, inp authInfo = JSON.parse(authInfoStr); const { action } = packageJson.features.authAPI; const func = extension[action]; - const config = (authInfo || []).reduce((acc, cur) => ({ [cur.key]: cur.value, ...acc }), {}); + const config = (authInfo || []).reduce((acc, cur) => ({ ...acc, [cur.key]: cur.value }), {}); //Execute at runtime const code = await func(config); - console.log(code); const [authPmRes, err] = await pmRuntime.executeSync(ctx, code, { context: pmRes }); @@ -548,11 +546,11 @@ privateFun.parseBeforeCode = async function (scritEngines = 'pm', inputData, inp tmpTargetTypeData = { apiUrl: apiUrl.toString(), bodyParam: pmRes.request.body.raw, - bodyParseParam: (pmRes.request.body.urlencoded || []).reduce((acc, cur) => ({ [cur.key]: cur.value, ...acc }), {}), - queryParam: (pmRes.request.url.query || []).reduce((acc, cur) => ({ [cur.key]: cur.value, ...acc }), {}), - headerParam: (pmRes.request.header || []).reduce((acc, cur) => ({ [cur.key]: cur.value, ...acc }), {}) + bodyParseParam: (pmRes.request.body.urlencoded || []).reduce((acc, cur) => ({ ...acc, [cur.key]: cur.value }), {}), + queryParam: (pmRes.request.url.query || []).reduce((acc, cur) => ({ ...acc, [cur.key]: cur.value }), {}), + headerParam: (pmRes.request.header || []).reduce((acc, cur) => ({ ...acc, [cur.key]: cur.value }), {}) }; - global.eoTestGlobals = (pmRes.globals.values || []).reduce((acc, cur) => ({ [cur.key]: cur.value, ...acc }), {}); + global.eoTestGlobals = (pmRes.globals.values || []).reduce((acc, cur) => ({ ...acc, [cur.key]: cur.value }), {}); //for fit eolink tmpOutput.url = apiUrl.toString().split('?')[0]; break; diff --git a/src/node/test-server/request/unit.js b/src/node/test-server/request/unit.js index 5557dee72..665338e93 100644 --- a/src/node/test-server/request/unit.js +++ b/src/node/test-server/request/unit.js @@ -715,7 +715,7 @@ const { resolve } = require('path'); value: typeof tmpDecorateObj.history.body[key][childKey] == 'string' ? tmpDecorateObj.history.body[key][childKey] - : '[file]' + : '(binary)' }); if (typeof tmpDecorateObj.history.body[key][childKey] != 'string') break; } diff --git a/src/platform/node/extension-manager/manager.ts b/src/platform/node/extension-manager/manager.ts index 5fcd4e0a1..50f455b57 100644 --- a/src/platform/node/extension-manager/manager.ts +++ b/src/platform/node/extension-manager/manager.ts @@ -1,5 +1,6 @@ import { createServer } from 'http-server/lib/http-server'; import { LanguageService } from 'pc/app/electron-main/language.service'; +import { defaultExtensions } from 'pc/browser/src/app/shared/models/extension'; import { ExtensionInfo, SidebarView, FeatureInfo } from 'pc/browser/src/app/shared/models/extension-manager'; import { isNotEmpty } from 'pc/shared/common/common'; import { HOME_DIR } from 'pc/shared/electron-main/constant'; @@ -17,7 +18,6 @@ import path from 'node:path'; const extServerMap = new Map(); // * npm pkg name -const defaultExtension = [{ name: 'postcat-export-openapi' }, { name: 'postcat-import-openapi' }, { name: 'postcat-basic-auth' }]; const isExists = async filePath => await promises .access(filePath) @@ -123,7 +123,7 @@ export class ModuleManager { const { extensions } = JSON.parse(debuggerExtension); debugExtension = extensions; } - const localExtensionName = [...new Set(list.map(it => it.name).concat(defaultExtension.map(it => it.name)))]; + const localExtensionName = [...new Set(list.map(it => it.name).concat(defaultExtensions))]; this.installExtension = remoteExtension .filter(it => localExtensionName.includes(it.name)) .filter(it => !debugExtension.includes(it.name)); diff --git a/src/shared/electron-main/constant.ts b/src/shared/electron-main/constant.ts index bc2f8a1df..084eaa83a 100644 --- a/src/shared/electron-main/constant.ts +++ b/src/shared/electron-main/constant.ts @@ -5,3 +5,15 @@ import * as path from 'path'; export const home: string = app.getPath('home'); export const HOME_DIR = path.join(home, '.postcat'); export const STORAGE_TEMP = path.join(HOME_DIR, 'tmp.storage'); + +export const GET_MODULES = 'getModules'; +export const GET_MODULE = 'getModule'; +export const INSTALL_MODULE = 'installModule'; +export const UNINSTALL_MODULE = 'uninstallModule'; +export const GET_FEATURE = 'getFeature'; +export const GET_MOCK_URL = 'getMockUrl'; +export const GET_WEBSOCKET_PORT = 'getWebsocketPort'; +export const GET_EXT_TABS = 'getExtTabs'; +export const LOGIN_WITH = 'loginWith'; +export const GET_SIDEBAR_VIEW = 'getSidebarView'; +export const GET_SIDEBAR_VIEWS = 'getSidebarViews'; diff --git a/test/e2e/src/env.spec.ts b/test/e2e/src/env.spec.ts index 4eff98a14..e7f4435c1 100644 --- a/test/e2e/src/env.spec.ts +++ b/test/e2e/src/env.spec.ts @@ -24,14 +24,20 @@ test.describe('Env Operate', () => { //Add first env will choose it //Edit env - await page.locator('body').press('Meta+c'); + await page.getByRole('tablist').locator('div').filter({ hasText: 'DEV' }).nth(2).hover(); await page.getByRole('button', { name: 'Close tab' }).click(); - await page.locator('div').filter({ hasText: 'DEV' }).nth(2).click(); + await page.locator('div').filter({ hasText: 'DEV' }).click(); await page.getByLabel('Name').press('Meta+s'); await ifTipsExist(page, 'Edited successfully'); //Delete env }); + + test('Env Delete', async ({ page }) => { + await page.locator('nz-tree-node-title div').first().hover(); + await page.locator('nz-tree-node-title').getByRole('button').click(); + await page.getByRole('button', { name: 'Delete' }).click(); + }); // test('Use Env', async ({ page }) => { // //Host uri // //Global variable diff --git a/test/e2e/test-results/src-user-User-Opeate-chromium/test-failed-1.png b/test/e2e/test-results/src-user-User-Opeate-chromium/test-failed-1.png new file mode 100644 index 000000000..55b7e24fe Binary files /dev/null and b/test/e2e/test-results/src-user-User-Opeate-chromium/test-failed-1.png differ diff --git a/yarn.lock b/yarn.lock index 54c4ebbcd..3ed616bf2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3774,6 +3774,11 @@ array-includes@^3.1.4: get-intrinsic "^1.1.3" is-string "^1.0.7" +array-keyed-map@^2.1.3: + version "2.1.3" + resolved "https://registry.npmmirror.com/array-keyed-map/-/array-keyed-map-2.1.3.tgz#0d5c06ed58ccba2755a4f5bfb7a935a31f395b18" + integrity sha512-JIUwuFakO+jHjxyp4YgSiKXSZeC0U+R1jR94bXWBcVlFRBycqXlb+kH9JHxBGcxnVuSqx5bnn0Qz9xtSeKOjiA== + array-union@^1.0.2: version "1.0.2" resolved "https://registry.npmmirror.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -8388,6 +8393,13 @@ got@^6.7.1: unzip-response "^2.0.1" url-parse-lax "^1.0.0" +gpt3-tokenizer@1.1.5: + version "1.1.5" + resolved "https://registry.npmmirror.com/gpt3-tokenizer/-/gpt3-tokenizer-1.1.5.tgz#79e5f96c435daf9c41a81117411ebce3bf475e78" + integrity sha512-O9iCL8MqGR0Oe9wTh0YftzIbysypNQmS5a5JG3cB3M4LMYjlAVvNnf8LUzVY9MrI7tj+YLY356uHtO2lLX2HpA== + dependencies: + array-keyed-map "^2.1.3" + graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"