diff --git a/package.json b/package.json index 5fd41996..ef173b9e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "eoapi", "souceLocale": "zh-Hans", - "version": "1.8.0", + "version": "1.8.1", "main": "out/app/electron-main/main.js", "description": "A lightweight, extensible API tool", "homepage": "https://github.com/eolinker/eoapi.git", diff --git a/src/platform/node/mock-server/index.ts b/src/platform/node/mock-server/index.ts index a1e8e4d1..a52e9cfa 100644 --- a/src/platform/node/mock-server/index.ts +++ b/src/platform/node/mock-server/index.ts @@ -90,6 +90,30 @@ export class MockServer { // next(); // } }); + this.app.all('/:workspaceID/:projectID/mock/:mockID/*', (req, res, next) => { + // if (!protocolReg.test(req.url)) { + // match request type + const isMatchType = this.configuration.getModuleSettings('eoapi-features.mock.matchType'); + if (req.params.mockID || isMatchType !== false) { + this.view.webContents.send('getMockApiList', JSON.parse(jsonStringify(req))); + ipcMain.once('getMockApiList', (event, message) => { + const { response = {}, statusCode = 200 } = message; + res.statusCode = statusCode; + if (res.statusCode === 404) { + this.send404(res, isMatchType); + } else { + res.send(response); + } + next(); + }); + } else { + this.send404(res, isMatchType); + next(); + } + // } else { + // next(); + // } + }); } /** diff --git a/src/workbench/browser/src/app/app.service.ts b/src/workbench/browser/src/app/app.service.ts index a6c1c5a3..48de8247 100644 --- a/src/workbench/browser/src/app/app.service.ts +++ b/src/workbench/browser/src/app/app.service.ts @@ -1,15 +1,20 @@ import { tree2obj } from './utils/tree/tree.utils'; import { Injectable } from '@angular/core'; import type { IpcRenderer } from 'electron'; -import { ApiData, ApiMockEntity } from 'eo/workbench/browser/src/app/shared/services/storage/index.model'; -import { IndexedDBStorage } from 'eo/workbench/browser/src/app/shared/services/storage/IndexedDB/lib/'; +import { + ApiData, + ApiMockEntity, + StorageRes, + StorageResStatus, +} from 'eo/workbench/browser/src/app/shared/services/storage/index.model'; +import { StorageService } from 'eo/workbench/browser/src/app/shared/services/storage/storage.service'; import { DataSourceService } from 'eo/workbench/browser/src/app/shared/services/data-source/data-source.service'; @Injectable() export class AppService { private ipcRenderer: IpcRenderer = window.require?.('electron')?.ipcRenderer; - constructor(private indexedDBStorage: IndexedDBStorage, private dataSource: DataSourceService) {} + constructor(private storageService: StorageService, private dataSource: DataSourceService) {} init() { if (this.ipcRenderer) { @@ -88,14 +93,12 @@ export class AppService { */ getMockByMockID(mockID: number): Promise { return new Promise((resolve, reject) => { - this.indexedDBStorage.mockLoad(mockID).subscribe( - (res: any) => { - resolve(res.data); - }, - (error: any) => { - reject(error); + this.storageService.run('mockLoad', [mockID], async (result: StorageRes) => { + if (result.status === StorageResStatus.success) { + return resolve(result.data); } - ); + return reject(result); + }); }); } /** @@ -106,14 +109,12 @@ export class AppService { */ getApiData(apiDataID: number): Promise { return new Promise((resolve, reject) => { - this.indexedDBStorage.apiDataLoad(apiDataID).subscribe( - (res: any) => { - resolve(res.data); - }, - (error: any) => { - reject(error); + this.storageService.run('apiDataLoad', [apiDataID], async (result: StorageRes) => { + if (result.status === StorageResStatus.success) { + return resolve(result.data); } - ); + return reject(result); + }); }); } /** @@ -121,14 +122,12 @@ export class AppService { */ getAllApi(projectID = 1): Promise { return new Promise((resolve, reject) => { - this.indexedDBStorage.apiDataLoadAllByProjectID(projectID).subscribe( - (res: any) => { - resolve(res.data); - }, - (error: any) => { - reject(error); + this.storageService.run('apiDataLoadAllByProjectID', [projectID], async (result: StorageRes) => { + if (result.status === StorageResStatus.success) { + return resolve(result.data); } - ); + return reject(result); + }); }); } } diff --git a/src/workbench/browser/src/app/pages/api/api.component.ts b/src/workbench/browser/src/app/pages/api/api.component.ts index cb4ced7f..400712a1 100644 --- a/src/workbench/browser/src/app/pages/api/api.component.ts +++ b/src/workbench/browser/src/app/pages/api/api.component.ts @@ -1,7 +1,7 @@ import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { StorageRes, StorageResStatus } from '../../shared/services/storage/index.model'; -import { Subject, takeUntil } from 'rxjs'; +import { filter, Subject, takeUntil } from 'rxjs'; import { Store } from '@ngxs/store'; import { Message, MessageService } from '../../shared/services/message'; import { StorageService } from '../../shared/services/storage'; @@ -75,8 +75,7 @@ export class ApiComponent implements OnInit, OnDestroy { private storage: StorageService, private electron: ElectronService, private store: Store - ) { - } + ) {} get envUuid(): number | null { return Number(localStorage.getItem('env:selected')) || 0; } @@ -137,9 +136,12 @@ export class ApiComponent implements OnInit, OnDestroy { * Get current API ID to show content tab */ watchRouterChange() { - this.router.events.pipe(takeUntil(this.destroy$)).subscribe(() => { - this.id = Number(this.route.snapshot.queryParams.uuid); - }); + this.router.events + .pipe(takeUntil(this.destroy$)) + .pipe(filter((event) => event instanceof NavigationEnd)) + .subscribe(() => { + this.id = Number(this.route.snapshot.queryParams.uuid); + }); } onSideResize({ width }: NzResizeEvent): void { diff --git a/src/workbench/browser/src/app/pages/api/group/edit/api-group-edit.component.ts b/src/workbench/browser/src/app/pages/api/group/edit/api-group-edit.component.ts index 0ed6ecfd..802d1023 100644 --- a/src/workbench/browser/src/app/pages/api/group/edit/api-group-edit.component.ts +++ b/src/workbench/browser/src/app/pages/api/group/edit/api-group-edit.component.ts @@ -115,7 +115,8 @@ export class ApiGroupEditComponent implements OnInit { if (result.status === StorageResStatus.success) { //delete group api if (data.api.length > 0) { - this.apiService.bulkDelete(data.api); + // this.apiService.bulkDelete(data.api); + this.messageService.send({ type: 'deleteApiSuccess', data: { uuids: data.api } }); } else { this.messageService.send({ type: 'updateGroupSuccess', data: {} }); } diff --git a/src/workbench/browser/src/app/pages/api/group/tree/api-group-tree.component.ts b/src/workbench/browser/src/app/pages/api/group/tree/api-group-tree.component.ts index b0e39764..4e5b6275 100644 --- a/src/workbench/browser/src/app/pages/api/group/tree/api-group-tree.component.ts +++ b/src/workbench/browser/src/app/pages/api/group/tree/api-group-tree.component.ts @@ -17,6 +17,7 @@ import { ElectronService } from '../../../../core/services'; import { ApiService } from 'eo/workbench/browser/src/app/pages/api/api.service'; import { ImportApiComponent } from 'eo/workbench/browser/src/app/shared/components/import-api/import-api.component'; import { EoMessageService } from 'eo/workbench/browser/src/app/eoui/message/eo-message.service'; +import { ProjectService } from 'eo/workbench/browser/src/app/shared/services/project/project.service'; @Component({ selector: 'eo-api-group-tree', templateUrl: './api-group-tree.component.html', @@ -73,6 +74,7 @@ export class ApiGroupTreeComponent implements OnInit, OnDestroy { private storage: StorageService, public electron: ElectronService, private apiService: ApiService, + private projectService: ProjectService, private nzModalService: NzModalService ) {} ngOnInit(): void { @@ -101,52 +103,53 @@ export class ApiGroupTreeComponent implements OnInit, OnDestroy { buildGroupTreeData = debounce(() => { this.groupByID = {}; this.treeItems = []; - this.getGroups(); + this.getProjectCollections(); }); - getGroups() { + + getProjectCollections() { this.apiDataLoading = true; - this.storage.run('groupLoadAllByProjectID', [this.projectID], (result: StorageRes) => { + this.storage.run('projectCollections', [this.projectService.currentProjectID], (result: StorageRes) => { if (result.status === StorageResStatus.success) { - result.data.forEach((item) => { - delete item.updatedAt; - this.groupByID[item.uuid] = item; - this.treeItems.push({ - title: item.name, - key: `group-${item.uuid}`, - weight: item.weight || 0, - parentID: item.parentID ? `group-${item.parentID}` : '0', - isLeaf: false, - }); - }); + const { groups, apis } = result.data; + this.getGroups(groups); + this.getApis(apis); } - this.getApis().finally(() => { - this.apiDataLoading = false; + this.apiDataLoading = false; + }); + } + + getGroups(apiGroups = []) { + apiGroups.forEach((item) => { + delete item.updatedAt; + this.groupByID[item.uuid] = item; + this.treeItems.push({ + title: item.name, + key: `group-${item.uuid}`, + weight: item.weight || 0, + parentID: item.parentID ? `group-${item.parentID}` : '0', + isLeaf: false, }); }); } - async getApis() { - const result: StorageRes = await this.apiService.getAll(this.projectID); - const { success, empty } = StorageResStatus; - if ([success, empty].includes(result.status)) { - const apiItems = {}; - [].concat(result.data).forEach((item: ApiData) => { - delete item.updatedAt; - apiItems[item.uuid] = item; - this.treeItems.push({ - title: item.name, - key: item.uuid.toString(), - weight: item.weight || 0, - parentID: item.groupID ? `group-${item.groupID}` : '0', - method: item.method, - isLeaf: true, - }); + async getApis(apis) { + const apiItems = {}; + [].concat(apis).forEach((item: ApiData) => { + delete item.updatedAt; + apiItems[item.uuid] = item; + this.treeItems.push({ + title: item.name, + key: item.uuid.toString(), + weight: item.weight || 0, + parentID: item.groupID ? `group-${item.groupID}` : '0', + method: item.method, + isLeaf: true, }); - this.apiDataItems = apiItems; - this.messageService.send({ type: 'loadApi', data: this.apiDataItems }); - this.setSelectedKeys(); - this.generateGroupTreeData(); - this.restoreExpandStatus(); - } + }); + this.apiDataItems = apiItems; + this.messageService.send({ type: 'loadApi', data: this.apiDataItems }); + this.setSelectedKeys(); + this.generateGroupTreeData(); + this.restoreExpandStatus(); } restoreExpandStatus() { const key = this.expandKeys.slice(0); diff --git a/src/workbench/browser/src/app/pages/api/http/test/result-response/api-test-result-response.component.ts b/src/workbench/browser/src/app/pages/api/http/test/result-response/api-test-result-response.component.ts index df7aeab7..1de5e359 100644 --- a/src/workbench/browser/src/app/pages/api/http/test/result-response/api-test-result-response.component.ts +++ b/src/workbench/browser/src/app/pages/api/http/test/result-response/api-test-result-response.component.ts @@ -41,8 +41,7 @@ export class ApiTestResultResponseComponent implements OnInit, OnChanges { } } } - ngOnInit(): void { - } + ngOnInit(): void {} contextMenu($event: MouseEvent, menu: NzDropdownMenuComponent): void { this.nzContextMenuService.create($event, menu); @@ -50,7 +49,7 @@ export class ApiTestResultResponseComponent implements OnInit, OnChanges { downloadResponseText() { this.blobUrl = getBlobUrl(this.model.body, this.model.contentType); - const blobFileName = decodeURI(this.model.blobFileName); + const blobFileName = decodeURI(this.model.blobFileName || 'test_response'); const tmpAElem = document.createElement('a'); if ('download' in tmpAElem) { tmpAElem.style.visibility = 'hidden'; diff --git a/src/workbench/browser/src/app/pages/api/tab/api-tab.component.html b/src/workbench/browser/src/app/pages/api/tab/api-tab.component.html index 378cb638..057a1534 100644 --- a/src/workbench/browser/src/app/pages/api/tab/api-tab.component.html +++ b/src/workbench/browser/src/app/pages/api/tab/api-tab.component.html @@ -1,56 +1,33 @@ - + -
+
- - {{ tabStorage.tabsByID.get(uuid).extends.method }} + + {{ tabStorage.tabsByID.get(uuid).extends.method + }} - {{ tabStorage.tabsByID.get(uuid).title }} + {{ tabStorage.tabsByID.get(uuid).title }}
- +
- @@ -59,8 +36,10 @@
  • Websocket
  • - - + +
      @@ -71,12 +50,8 @@
    • Close Tabs To the Left
    • -
    • +
    • Close Tabs to the Right
    diff --git a/src/workbench/browser/src/app/shared/components/extension-select/extension-select.component.html b/src/workbench/browser/src/app/shared/components/extension-select/extension-select.component.html index 2d936adb..1c825e80 100644 --- a/src/workbench/browser/src/app/shared/components/extension-select/extension-select.component.html +++ b/src/workbench/browser/src/app/shared/components/extension-select/extension-select.component.html @@ -23,7 +23,7 @@
    - This feature requires plugin support, please move to Extensions download + This feature requires plugin support, please move to Extensions download or open exist extensions.
    diff --git a/src/workbench/browser/src/app/shared/services/storage/IndexedDB/lib/index.ts b/src/workbench/browser/src/app/shared/services/storage/IndexedDB/lib/index.ts index b7240a5c..76495849 100644 --- a/src/workbench/browser/src/app/shared/services/storage/IndexedDB/lib/index.ts +++ b/src/workbench/browser/src/app/shared/services/storage/IndexedDB/lib/index.ts @@ -654,6 +654,24 @@ export class IndexedDBStorage extends Dexie implements StorageInterface { groupLoadAllByProjectID(projectID: number | string): Observable { return this.loadAllByConditions(this.group, { projectID }); } + /** + * Load project collections + * @param projectID + * @returns + */ + projectCollections(projectID: number | string): Observable { + return new Observable((obs) => { + const fun = async () => { + const result = { + groups: await this.group.where({ projectID }).toArray(), + apis: await this.apiData.where({ projectID }).toArray(), + }; + obs.next(this.resProxy(result)); + obs.complete(); + }; + fun(); + }); + } /** * Delete group item. diff --git a/src/workbench/browser/src/app/shared/services/storage/http/lib/index.ts b/src/workbench/browser/src/app/shared/services/storage/http/lib/index.ts index 21adb8aa..d4b42fdb 100644 --- a/src/workbench/browser/src/app/shared/services/storage/http/lib/index.ts +++ b/src/workbench/browser/src/app/shared/services/storage/http/lib/index.ts @@ -23,6 +23,10 @@ export class HttpStorage implements StorageInterface { projectImport(uuid: number, item: Project) { return this.http.put(`/project/${uuid}/import`, item) as Observable; } + // Project collections + projectCollections(uuid: number) { + return this.http.get(`/project/${uuid}/collections`) as Observable; + } projectCreate(item: Project) { return this.http.post(`/project`, item) as Observable; } diff --git a/src/workbench/browser/src/app/utils/index.utils.ts b/src/workbench/browser/src/app/utils/index.utils.ts index c6823c42..6d9840b2 100644 --- a/src/workbench/browser/src/app/utils/index.utils.ts +++ b/src/workbench/browser/src/app/utils/index.utils.ts @@ -122,7 +122,7 @@ const base64ToUint8Array = (inputBase64String) => { export const getBlobUrl = (inputStream, inputFileType) => { let tmpBlob; try { - inputStream = base64ToUint8Array(inputStream); + // inputStream = base64ToUint8Array(inputStream); if (typeof window.Blob === 'function') { tmpBlob = new Blob([inputStream], { type: inputFileType, diff --git a/src/workbench/browser/src/app/utils/storage/Storage.ts b/src/workbench/browser/src/app/utils/storage/Storage.ts index b2b5a15c..a2b8ec21 100644 --- a/src/workbench/browser/src/app/utils/storage/Storage.ts +++ b/src/workbench/browser/src/app/utils/storage/Storage.ts @@ -61,7 +61,6 @@ export const createStorage = ({ prefixKey = '', storage = localStorage } = {}) = * @param {string} key */ remove(key: string) { - console.log(key, '搜索'); this.storage.removeItem(this.getKey(key)); } diff --git a/src/workbench/node/request/unit.js b/src/workbench/node/request/unit.js index 7cc38bfd..f4e66b31 100644 --- a/src/workbench/node/request/unit.js +++ b/src/workbench/node/request/unit.js @@ -417,12 +417,12 @@ const { resolve } = require('path'); tmpFileBinary, blobFileName; try { - blobFileName = _ContentDisposition.parse(inputRes.headers['content-disposition'] || 'undefined') + blobFileName = _ContentDisposition.parse(inputRes.headers['content-disposition'] || 'test_response') .parameters.filename; } catch (PARSE_CONTENT_DISPOSITION_ERR) { try { blobFileName = _ContentDisposition.parse( - encodeURI(inputRes.headers['content-disposition'] || 'undefined').replace(/\?/g, '') + encodeURI(inputRes.headers['content-disposition'] || 'test_response').replace(/\?/g, '') ).parameters.filename; } catch (URL_ENCODE_PARSE_CONTENT_DISPOSITION_ERR) {} }