Files
hoppscotch/packages/hoppscotch-common/src/helpers/collection/collection.ts

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
}