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"