feat: extend duplicate collection to personal workspace in SH (#4368)

This commit is contained in:
James George
2024-09-30 08:50:19 -07:00
committed by GitHub
parent 58857be650
commit 5e9f8743d4
12 changed files with 624 additions and 295 deletions

View File

@@ -39,6 +39,7 @@ import {
UserCollectionRemovedDocument,
UserCollectionMovedDocument,
UserCollectionOrderUpdatedDocument,
UserCollectionDuplicatedDocument,
ExportUserCollectionsToJsonQuery,
ExportUserCollectionsToJsonQueryVariables,
ExportUserCollectionsToJsonDocument,
@@ -328,6 +329,12 @@ export const runUserCollectionOrderUpdatedSubscription = () =>
variables: {},
})
export const runUserCollectionDuplicatedSubscription = () =>
runGQLSubscription({
query: UserCollectionDuplicatedDocument,
variables: {},
})
export const runUserRequestCreatedSubscription = () =>
runGQLSubscription({ query: UserRequestCreatedDocument, variables: {} })

View File

@@ -1,10 +1,11 @@
import { authEvents$, def as platformAuth } from "@platform/auth/auth.platform"
import { CollectionsPlatformDef } from "@hoppscotch/common/platform/collections"
import { authEvents$, def as platformAuth } from "@platform/auth/auth.platform"
import { runDispatchWithOutSyncing } from "../../lib/sync"
import {
exportUserCollectionsToJSON,
runUserCollectionCreatedSubscription,
runUserCollectionDuplicatedSubscription,
runUserCollectionMovedSubscription,
runUserCollectionOrderUpdatedSubscription,
runUserCollectionRemovedSubscription,
@@ -16,37 +17,36 @@ import {
} from "./collections.api"
import { collectionsSyncer, getStoreByCollectionType } from "./collections.sync"
import * as E from "fp-ts/Either"
import {
addRESTCollection,
setRESTCollections,
editRESTCollection,
removeRESTCollection,
moveRESTFolder,
updateRESTCollectionOrder,
saveRESTRequestAs,
navigateToFolderWithIndexPath,
editRESTRequest,
removeRESTRequest,
moveRESTRequest,
updateRESTRequestOrder,
addRESTFolder,
editRESTFolder,
removeRESTFolder,
addGraphqlFolder,
addGraphqlCollection,
editGraphqlFolder,
editGraphqlCollection,
removeGraphqlFolder,
removeGraphqlCollection,
saveGraphqlRequestAs,
editGraphqlRequest,
moveGraphqlRequest,
removeGraphqlRequest,
setGraphqlCollections,
restCollectionStore,
} from "@hoppscotch/common/newstore/collections"
import { runGQLSubscription } from "@hoppscotch/common/helpers/backend/GQLClient"
import {
addGraphqlCollection,
addGraphqlFolder,
addRESTCollection,
addRESTFolder,
editGraphqlCollection,
editGraphqlFolder,
editGraphqlRequest,
editRESTCollection,
editRESTFolder,
editRESTRequest,
moveGraphqlRequest,
moveRESTFolder,
moveRESTRequest,
navigateToFolderWithIndexPath,
removeGraphqlCollection,
removeGraphqlFolder,
removeGraphqlRequest,
removeRESTCollection,
removeRESTFolder,
removeRESTRequest,
restCollectionStore,
saveGraphqlRequestAs,
saveRESTRequestAs,
setGraphqlCollections,
setRESTCollections,
updateRESTCollectionOrder,
updateRESTRequestOrder,
} from "@hoppscotch/common/newstore/collections"
import {
GQLHeader,
HoppCollection,
@@ -55,8 +55,13 @@ import {
HoppRESTParam,
HoppRESTRequest,
} from "@hoppscotch/data"
import * as E from "fp-ts/Either"
import {
ReqType,
UserCollectionDuplicatedData,
UserRequest,
} from "../../api/generated/graphql"
import { gqlCollectionsSyncer } from "./gqlCollections.sync"
import { ReqType } from "../../api/generated/graphql"
function initCollectionsSync() {
const currentUser$ = platformAuth.getCurrentUserStream()
@@ -269,6 +274,9 @@ function setupSubscriptions() {
const userCollectionMovedSub = setupUserCollectionMovedSubscription()
const userCollectionOrderUpdatedSub =
setupUserCollectionOrderUpdatedSubscription()
const userCollectionDuplicatedSub =
setupUserCollectionDuplicatedSubscription()
const userRequestCreatedSub = setupUserRequestCreatedSubscription()
const userRequestUpdatedSub = setupUserRequestUpdatedSubscription()
const userRequestDeletedSub = setupUserRequestDeletedSubscription()
@@ -280,6 +288,7 @@ function setupSubscriptions() {
userCollectionRemovedSub,
userCollectionMovedSub,
userCollectionOrderUpdatedSub,
userCollectionDuplicatedSub,
userRequestCreatedSub,
userRequestUpdatedSub,
userRequestDeletedSub,
@@ -314,20 +323,6 @@ function setupUserCollectionCreatedSubscription() {
return
}
// While duplicating a collection, the new entry added to the store has an ID with a suffix to be updated after the backend ID is received from the GQL subscription
// This is to prevent the new entry from being added to the store again when the GQL subscription
// The boolean return value indicates if the GQL subscription was fired because of a duplicate collection action and whether the collection should be added to the store
const shouldCreateCollection = issueBackendIDToDuplicatedCollection(
collectionStore,
collectionType,
userCollectionBackendID,
parentCollectionID
)
if (!shouldCreateCollection) {
return
}
const parentCollectionPath =
parentCollectionID &&
getCollectionPathFromCollectionID(
@@ -566,6 +561,147 @@ function setupUserCollectionOrderUpdatedSubscription() {
return userCollectionOrderUpdatedSub
}
function setupUserCollectionDuplicatedSubscription() {
const [userCollectionDuplicated$, userCollectionDuplicatedSub] =
runUserCollectionDuplicatedSubscription()
userCollectionDuplicated$.subscribe((res) => {
if (E.isRight(res)) {
const {
childCollections: childCollectionsJSONStr,
data,
id,
parentID: parentCollectionID,
requests: userRequests,
title: name,
type: collectionType,
} = res.right.userCollectionDuplicated
const { collectionStore } = getStoreByCollectionType(collectionType)
const parentCollectionPath =
parentCollectionID &&
getCollectionPathFromCollectionID(
parentCollectionID,
collectionStore.value.state
)
// Incoming data transformed to the respective internal representations
const { auth, headers } =
data && data != "null"
? JSON.parse(data)
: {
auth: { authType: "inherit", authActive: false },
headers: [],
}
const folders = transformDuplicatedCollections(childCollectionsJSONStr)
const requests = transformDuplicatedCollectionRequests(
userRequests as UserRequest[]
)
// New collection to be added to store with the transformed data
const effectiveDuplicatedCollection: HoppCollection = {
id,
name,
folders,
requests,
v: 3,
auth,
headers: addDescriptionField(headers),
}
// only folders will have parent collection id
if (parentCollectionID && parentCollectionPath) {
const collectionCreatedFromStoreIDSuffix = "-duplicate-collection"
const parentCollection = navigateToFolderWithIndexPath(
collectionStore.value.state,
parentCollectionPath
.split("/")
.map((pathIndex) => parseInt(pathIndex))
)
if (!parentCollection) {
return
}
// Grab the child collection inserted via store update with the ID suffix
const collectionInsertedViaStoreUpdateIdx =
parentCollection.folders.findIndex(({ id }) =>
id?.endsWith(collectionCreatedFromStoreIDSuffix)
)
if (collectionInsertedViaStoreUpdateIdx === -1) {
return
}
const collectionInsertedViaStoreUpdateIndexPath = `${parentCollectionPath}/${collectionInsertedViaStoreUpdateIdx}`
runDispatchWithOutSyncing(() => {
/**
* Step 1. Remove the collection inserted via store update with the ID suffix
* Step 2. Add the duplicated collection received from the GQL subscription
* Step 3. Update the duplicated collection with the relevant data
*/
if (collectionType === "GQL") {
removeGraphqlFolder(collectionInsertedViaStoreUpdateIndexPath)
addGraphqlFolder(name, parentCollectionPath)
editGraphqlFolder(
collectionInsertedViaStoreUpdateIndexPath,
effectiveDuplicatedCollection
)
} else {
removeRESTFolder(collectionInsertedViaStoreUpdateIndexPath)
addRESTFolder(name, parentCollectionPath)
editRESTFolder(
collectionInsertedViaStoreUpdateIndexPath,
effectiveDuplicatedCollection
)
}
})
} else {
// root collections won't have `parentCollectionID`
const collectionCreatedFromStoreIDSuffix = "-duplicate-collection"
// Grab the child collection inserted via store update with the ID suffix
const collectionInsertedViaStoreUpdateIdx =
collectionStore.value.state.findIndex(({ id }) =>
id?.endsWith(collectionCreatedFromStoreIDSuffix)
)
if (collectionInsertedViaStoreUpdateIdx === -1) {
return
}
runDispatchWithOutSyncing(() => {
/**
* Step 1. Remove the collection inserted via store update with the ID suffix
* Step 2. Add the duplicated collection received from the GQL subscription
*/
if (collectionType === "GQL") {
removeGraphqlCollection(collectionInsertedViaStoreUpdateIdx)
addGraphqlCollection(effectiveDuplicatedCollection)
} else {
removeRESTCollection(collectionInsertedViaStoreUpdateIdx)
addRESTCollection(effectiveDuplicatedCollection)
}
})
}
}
})
return userCollectionDuplicatedSub
}
function setupUserRequestCreatedSubscription() {
const [userRequestCreated$, userRequestCreatedSub] =
runUserRequestCreatedSubscription()
@@ -873,104 +1009,51 @@ function getRequestIndex(
return requestIndex
}
function issueBackendIDToDuplicatedCollection(
collectionStore: ReturnType<
typeof getStoreByCollectionType
>["collectionStore"],
collectionType: ReqType,
userCollectionBackendID: string,
parentCollectionID?: string
): boolean {
// Collection added to store via duplicating is set an ID with a suffix to be updated after the backend ID is received from the GQL subscription
const collectionCreatedFromStoreIDSuffix = "-duplicate-collection"
function transformDuplicatedCollections(
collectionsJSONStr: string
): HoppCollection[] {
const parsedCollections: UserCollectionDuplicatedData[] =
JSON.parse(collectionsJSONStr)
// Duplicating a child collection
if (parentCollectionID) {
// Get the index path for the parent collection
const parentCollectionPath = getCollectionPathFromCollectionID(
parentCollectionID,
collectionStore.value.state
)
return parsedCollections.map(
({
childCollections: childCollectionsJSONStr,
data,
id,
requests: userRequests,
title: name,
}) => {
const { auth, headers } =
data && data !== "null"
? JSON.parse(data)
: { auth: { authType: "inherit", authActive: false }, headers: [] }
if (!parentCollectionPath) {
// Indicates the collection received from the GQL subscription should be created in the store
return true
}
const folders = transformDuplicatedCollections(childCollectionsJSONStr)
const parentCollection = navigateToFolderWithIndexPath(
collectionStore.value.state,
parentCollectionPath.split("/").map((index) => parseInt(index))
)
const requests = transformDuplicatedCollectionRequests(userRequests)
if (!parentCollection) {
// Indicates the collection received from the GQL subscription should be created in the store
return true
}
// Grab the child collection inserted via store update with the ID suffix
const collectionInsertedViaStoreUpdateIdx =
parentCollection.folders.findIndex(({ id }) =>
id?.endsWith(collectionCreatedFromStoreIDSuffix)
)
// No entry indicates the GQL subscription was fired not because of a duplicate collection action
if (collectionInsertedViaStoreUpdateIdx === -1) {
// Indicates the collection received from the GQL subscription should be created in the store
return true
}
const collectionInsertedViaStoreUpdate =
parentCollection.folders[collectionInsertedViaStoreUpdateIdx]
const childCollectionPath = `${parentCollectionPath}/${collectionInsertedViaStoreUpdateIdx}`
// Update the ID for the child collection already existing in store with the backend ID
runDispatchWithOutSyncing(() => {
if (collectionType == ReqType.Rest) {
editRESTFolder(childCollectionPath, {
...collectionInsertedViaStoreUpdate,
id: userCollectionBackendID,
})
} else {
editGraphqlFolder(childCollectionPath, {
...collectionInsertedViaStoreUpdate,
id: userCollectionBackendID,
})
return {
id,
name,
folders,
requests,
v: 3,
auth,
headers: addDescriptionField(headers),
}
})
} else {
// Duplicating a root collection
// Grab the collection inserted via store update with the ID suffix
const collectionInsertedViaStoreUpdateIdx =
collectionStore.value.state.findIndex(({ id }) =>
id?.endsWith(collectionCreatedFromStoreIDSuffix)
)
// No entry indicates the GQL subscription was fired not because of a duplicate collection action
if (collectionInsertedViaStoreUpdateIdx === -1) {
// Indicates the collection received from the GQL subscription should be created in the store
return true
}
const collectionInsertedViaStoreUpdate =
collectionStore.value.state[collectionInsertedViaStoreUpdateIdx]
// Update the ID for the collection already existing in store with the backend ID
runDispatchWithOutSyncing(() => {
if (collectionType == ReqType.Rest) {
editRESTCollection(collectionInsertedViaStoreUpdateIdx, {
...collectionInsertedViaStoreUpdate,
id: userCollectionBackendID,
})
} else {
editGraphqlCollection(collectionInsertedViaStoreUpdateIdx, {
...collectionInsertedViaStoreUpdate,
id: userCollectionBackendID,
})
}
})
}
// Prevent adding the collection received from GQL subscription to the store
return false
)
}
function transformDuplicatedCollectionRequests(
requests: UserRequest[]
): HoppRESTRequest[] | HoppGQLRequest[] {
return requests.map(({ id, request }) => {
const parsedRequest = JSON.parse(request)
return {
...parsedRequest,
id,
}
})
}