294 lines
8.8 KiB
TypeScript
294 lines
8.8 KiB
TypeScript
import { HoppCollection } from "@hoppscotch/data"
|
|
import { getAffectedIndexes } from "./affectedIndex"
|
|
import { GetSingleRequestDocument } from "../backend/graphql"
|
|
import { runGQLQuery } from "../backend/GQLClient"
|
|
import * as E from "fp-ts/Either"
|
|
import { getService } from "~/modules/dioc"
|
|
import { RESTTabService } from "~/services/tab/rest"
|
|
import { HoppInheritedProperty } from "../types/HoppInheritedProperties"
|
|
import { GQLTabService } from "~/services/tab/graphql"
|
|
|
|
/**
|
|
* Resolve save context on reorder
|
|
* @param payload
|
|
* @param payload.lastIndex
|
|
* @param payload.newIndex
|
|
* @param folderPath
|
|
* @param payload.length
|
|
* @returns
|
|
*/
|
|
|
|
export function resolveSaveContextOnCollectionReorder(
|
|
payload: {
|
|
lastIndex: number
|
|
newIndex: number
|
|
folderPath: string
|
|
length?: number // better way to do this? now it could be undefined
|
|
},
|
|
type: "remove" | "drop" = "remove"
|
|
) {
|
|
const { lastIndex, folderPath, length } = payload
|
|
let { newIndex } = payload
|
|
|
|
if (newIndex > lastIndex) newIndex-- // there is a issue when going down? better way to resolve this?
|
|
if (lastIndex === newIndex) return
|
|
|
|
const affectedIndexes = getAffectedIndexes(
|
|
lastIndex,
|
|
newIndex === -1 ? length! : newIndex
|
|
)
|
|
|
|
if (newIndex === -1) {
|
|
// if (newIndex === -1) remove it from the map because it will be deleted
|
|
affectedIndexes.delete(lastIndex)
|
|
// when collection deleted opended requests from that collection be affected
|
|
if (type === "remove") {
|
|
resetSaveContextForAffectedRequests(
|
|
folderPath ? `${folderPath}/${lastIndex}` : lastIndex.toString()
|
|
)
|
|
}
|
|
}
|
|
|
|
// add folder path as prefix to the affected indexes
|
|
const affectedPaths = new Map<string, string>()
|
|
for (const [key, value] of affectedIndexes) {
|
|
if (folderPath) {
|
|
affectedPaths.set(`${folderPath}/${key}`, `${folderPath}/${value}`)
|
|
} else {
|
|
affectedPaths.set(key.toString(), value.toString())
|
|
}
|
|
}
|
|
|
|
const tabService = getService(RESTTabService)
|
|
|
|
const tabs = tabService.getTabsRefTo((tab) => {
|
|
return (
|
|
tab.document.saveContext?.originLocation === "user-collection" &&
|
|
affectedPaths.has(tab.document.saveContext.folderPath)
|
|
)
|
|
})
|
|
|
|
for (const tab of tabs) {
|
|
if (tab.value.document.saveContext?.originLocation === "user-collection") {
|
|
const newPath = affectedPaths.get(
|
|
tab.value.document.saveContext?.folderPath
|
|
)!
|
|
tab.value.document.saveContext.folderPath = newPath
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resolve save context for affected requests on drop folder from one to another
|
|
* @param oldFolderPath
|
|
* @param newFolderPath
|
|
* @returns
|
|
*/
|
|
|
|
export function updateSaveContextForAffectedRequests(
|
|
oldFolderPath: string,
|
|
newFolderPath: string
|
|
) {
|
|
const tabService = getService(RESTTabService)
|
|
const tabs = tabService.getTabsRefTo((tab) => {
|
|
return (
|
|
tab.document.saveContext?.originLocation === "user-collection" &&
|
|
tab.document.saveContext.folderPath.startsWith(oldFolderPath)
|
|
)
|
|
})
|
|
|
|
for (const tab of tabs) {
|
|
if (tab.value.document.saveContext?.originLocation === "user-collection") {
|
|
tab.value.document.saveContext = {
|
|
...tab.value.document.saveContext,
|
|
folderPath: tab.value.document.saveContext.folderPath.replace(
|
|
oldFolderPath,
|
|
newFolderPath
|
|
),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Used to check the new folder path is close to the save context folder path or not
|
|
* @param folderPathCurrent The path saved as the inherited path in the inherited properties
|
|
* @param newFolderPath The incomming path
|
|
* @param saveContextPath The save context of the request
|
|
* @returns The path which is close to saveContext.folderPath
|
|
*/
|
|
function folderPathCloseToSaveContext(
|
|
folderPathCurrent: string | undefined,
|
|
newFolderPath: string,
|
|
saveContextPath: string
|
|
) {
|
|
if (!folderPathCurrent) return newFolderPath
|
|
|
|
const folderPathCurrentArray = folderPathCurrent.split("/")
|
|
const newFolderPathArray = newFolderPath.split("/")
|
|
const saveContextFolderPathArray = saveContextPath.split("/")
|
|
|
|
const folderPathCurrentMatch = folderPathCurrentArray.filter(
|
|
(folder, i) => folder === saveContextFolderPathArray[i]
|
|
).length
|
|
|
|
const newFolderPathMatch = newFolderPathArray.filter(
|
|
(folder, i) => folder === saveContextFolderPathArray[i]
|
|
).length
|
|
|
|
return folderPathCurrentMatch > newFolderPathMatch
|
|
? folderPathCurrent
|
|
: newFolderPath
|
|
}
|
|
|
|
function removeDuplicatesAndKeepLast(arr: HoppInheritedProperty["headers"]) {
|
|
const keyMap: { [key: string]: number[] } = {} // Map to store array of indices for each key
|
|
|
|
// Populate keyMap with the indices of each key
|
|
arr.forEach((item, index) => {
|
|
const key = item.inheritedHeader.key
|
|
if (!(key in keyMap)) {
|
|
keyMap[key] = []
|
|
}
|
|
keyMap[key].push(index)
|
|
})
|
|
|
|
// Create a new array containing only the last occurrence of each key
|
|
const result = []
|
|
for (const key in keyMap) {
|
|
if (Object.prototype.hasOwnProperty.call(keyMap, key)) {
|
|
const lastIndex = keyMap[key][keyMap[key].length - 1]
|
|
result.push(arr[lastIndex])
|
|
}
|
|
}
|
|
|
|
// Sort the result array based on the parentID
|
|
result.sort((a, b) => a.parentID.localeCompare(b.parentID))
|
|
return result
|
|
}
|
|
|
|
export function updateInheritedPropertiesForAffectedRequests(
|
|
path: string,
|
|
inheritedProperties: HoppInheritedProperty,
|
|
type: "rest" | "graphql"
|
|
) {
|
|
const tabService =
|
|
type === "rest" ? getService(RESTTabService) : getService(GQLTabService)
|
|
|
|
const effectedTabs = tabService.getTabsRefTo((tab) => {
|
|
const saveContext = tab.document.saveContext
|
|
|
|
const saveContextPath =
|
|
saveContext?.originLocation === "team-collection"
|
|
? saveContext.collectionID
|
|
: saveContext?.folderPath
|
|
|
|
return saveContextPath?.startsWith(path) ?? false
|
|
})
|
|
|
|
effectedTabs.map((tab) => {
|
|
const inheritedParentID =
|
|
tab.value.document.inheritedProperties?.auth.parentID
|
|
|
|
const contextPath =
|
|
tab.value.document.saveContext?.originLocation === "team-collection"
|
|
? tab.value.document.saveContext.collectionID
|
|
: tab.value.document.saveContext?.folderPath
|
|
|
|
const effectedPath = folderPathCloseToSaveContext(
|
|
inheritedParentID,
|
|
path,
|
|
contextPath ?? ""
|
|
)
|
|
|
|
if (effectedPath === path) {
|
|
if (tab.value.document.inheritedProperties) {
|
|
tab.value.document.inheritedProperties.auth = inheritedProperties.auth
|
|
}
|
|
}
|
|
|
|
if (tab.value.document.inheritedProperties?.headers) {
|
|
// filter out the headers with the parentID not as the path
|
|
const headers = tab.value.document.inheritedProperties.headers.filter(
|
|
(header) => header.parentID !== path
|
|
)
|
|
|
|
// filter out the headers with the parentID as the path in the inheritedProperties
|
|
const inheritedHeaders = inheritedProperties.headers.filter(
|
|
(header) => header.parentID === path
|
|
)
|
|
|
|
// merge the headers with the parentID as the path
|
|
const mergedHeaders = removeDuplicatesAndKeepLast([
|
|
...new Set([...inheritedHeaders, ...headers]),
|
|
])
|
|
|
|
tab.value.document.inheritedProperties.headers = mergedHeaders
|
|
}
|
|
})
|
|
}
|
|
|
|
function resetSaveContextForAffectedRequests(folderPath: string) {
|
|
const tabService = getService(RESTTabService)
|
|
const tabs = tabService.getTabsRefTo((tab) => {
|
|
return (
|
|
tab.document.saveContext?.originLocation === "user-collection" &&
|
|
tab.document.saveContext.folderPath.startsWith(folderPath)
|
|
)
|
|
})
|
|
|
|
for (const tab of tabs) {
|
|
tab.value.document.saveContext = null
|
|
tab.value.document.isDirty = true
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reset save context to null if requests are deleted from the team collection or its folder
|
|
* only runs when collection or folder is deleted
|
|
*/
|
|
|
|
export async function resetTeamRequestsContext() {
|
|
const tabService = getService(RESTTabService)
|
|
const tabs = tabService.getTabsRefTo((tab) => {
|
|
return tab.document.saveContext?.originLocation === "team-collection"
|
|
})
|
|
|
|
for (const tab of tabs) {
|
|
if (tab.value.document.saveContext?.originLocation === "team-collection") {
|
|
const data = await runGQLQuery({
|
|
query: GetSingleRequestDocument,
|
|
variables: {
|
|
requestID: tab.value.document.saveContext?.requestID,
|
|
},
|
|
})
|
|
|
|
if (E.isRight(data) && data.right.request === null) {
|
|
tab.value.document.saveContext = null
|
|
tab.value.document.isDirty = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export function getFoldersByPath(
|
|
collections: HoppCollection[],
|
|
path: string
|
|
): HoppCollection[] {
|
|
if (!path) return collections
|
|
|
|
// path will be like this "0/0/1" these are the indexes of the folders
|
|
const pathArray = path.split("/").map((index) => parseInt(index))
|
|
|
|
let currentCollection = collections[pathArray[0]]
|
|
|
|
if (pathArray.length === 1) {
|
|
return currentCollection.folders
|
|
}
|
|
for (let i = 1; i < pathArray.length; i++) {
|
|
const folder = currentCollection.folders[pathArray[i]]
|
|
if (folder) currentCollection = folder
|
|
}
|
|
|
|
return currentCollection.folders
|
|
}
|