feat: collection level headers and authorization (#3505)

Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
This commit is contained in:
Nivedin
2023-12-13 22:43:18 +05:30
committed by GitHub
parent f3edd001d7
commit 47e009267b
95 changed files with 3221 additions and 970 deletions

View File

@@ -57,7 +57,10 @@
@edit-request="editRequest($event)"
@duplicate-request="duplicateRequest($event)"
@select-collection="$emit('use-collection', collection)"
@edit-properties="editProperties($event)"
@select="$emit('select', $event)"
@select-request="selectRequest($event)"
@drop-request="dropRequest($event)"
/>
</div>
<HoppSmartPlaceholder
@@ -142,19 +145,27 @@
v-if="showModalImportExport"
@hide-modal="displayModalImportExport(false)"
/>
<CollectionsProperties
:show="showModalEditProperties"
:editing-properties="editingProperties"
@hide-modal="displayModalEditProperties(false)"
@set-collection-properties="setCollectionProperties"
/>
</div>
</template>
<script lang="ts">
// TODO: TypeScript + Script Setup this :)
import { defineComponent } from "vue"
import { cloneDeep, clone } from "lodash-es"
<script setup lang="ts">
import { nextTick, ref } from "vue"
import { clone, cloneDeep } from "lodash-es"
import {
graphqlCollections$,
addGraphqlFolder,
saveGraphqlRequestAs,
cascadeParentCollectionForHeaderAuth,
editGraphqlCollection,
editGraphqlFolder,
moveGraphqlRequest,
} from "~/newstore/collections"
import IconPlus from "~icons/lucide/plus"
import IconHelpCircle from "~icons/lucide/help-circle"
import IconImport from "~icons/lucide/folder-down"
@@ -164,213 +175,448 @@ import { useColorMode } from "@composables/theming"
import { platform } from "~/platform"
import { useService } from "dioc/vue"
import { GQLTabService } from "~/services/tab/graphql"
import { computed } from "vue"
import {
HoppCollection,
HoppGQLRequest,
makeGQLRequest,
} from "@hoppscotch/data"
import { Picked } from "~/helpers/types/HoppPicked"
import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties"
import { updateInheritedPropertiesForAffectedRequests } from "~/helpers/collection/collection"
import { useToast } from "~/composables/toast"
import { getRequestsByPath } from "~/helpers/collection/request"
export default defineComponent({
props: {
// Whether to activate the ability to pick items (activates 'select' events)
saveRequest: { type: Boolean, default: false },
picked: { type: Object, default: null },
},
emits: ["select", "use-collection"],
setup() {
const collections = useReadonlyStream(graphqlCollections$, [], "deep")
const colorMode = useColorMode()
const t = useI18n()
const tabs = useService(GQLTabService)
const t = useI18n()
const toast = useToast()
return {
collections,
colorMode,
t,
tabs,
IconPlus,
IconHelpCircle,
IconImport,
}
},
data() {
return {
showModalAdd: false,
showModalEdit: false,
showModalImportExport: false,
showModalAddRequest: false,
showModalAddFolder: false,
showModalEditFolder: false,
showModalEditRequest: false,
editingCollection: undefined,
editingCollectionIndex: undefined,
editingFolder: undefined,
editingFolderName: undefined,
editingFolderIndex: undefined,
editingFolderPath: undefined,
editingRequest: undefined,
editingRequestIndex: undefined,
filterText: "",
}
},
computed: {
filteredCollections() {
const collections = clone(this.collections)
defineProps<{
// Whether to activate the ability to pick items (activates 'select' events)
saveRequest: boolean
picked: Picked
}>()
if (!this.filterText) return collections
const collections = useReadonlyStream(graphqlCollections$, [], "deep")
const colorMode = useColorMode()
const tabs = useService(GQLTabService)
const filterText = this.filterText.toLowerCase()
const filteredCollections = []
const showModalAdd = ref(false)
const showModalEdit = ref(false)
const showModalImportExport = ref(false)
const showModalAddRequest = ref(false)
const showModalAddFolder = ref(false)
const showModalEditFolder = ref(false)
const showModalEditRequest = ref(false)
const showModalEditProperties = ref(false)
for (const collection of collections) {
const filteredRequests = []
const filteredFolders = []
for (const request of collection.requests) {
if (request.name.toLowerCase().includes(filterText))
filteredRequests.push(request)
}
for (const folder of collection.folders) {
const filteredFolderRequests = []
for (const request of folder.requests) {
if (request.name.toLowerCase().includes(filterText))
filteredFolderRequests.push(request)
}
if (filteredFolderRequests.length > 0) {
const filteredFolder = Object.assign({}, folder)
filteredFolder.requests = filteredFolderRequests
filteredFolders.push(filteredFolder)
}
}
const editingCollection = ref<HoppCollection | null>(null)
const editingCollectionIndex = ref<number | null>(null)
const editingFolder = ref<HoppCollection | null>(null)
const editingFolderName = ref("")
const editingFolderIndex = ref<number | null>(null)
const editingFolderPath = ref("")
const editingRequest = ref<HoppGQLRequest | null>(null)
const editingRequestIndex = ref<number | null>(null)
if (filteredRequests.length + filteredFolders.length > 0) {
const filteredCollection = Object.assign({}, collection)
filteredCollection.requests = filteredRequests
filteredCollection.folders = filteredFolders
filteredCollections.push(filteredCollection)
}
}
return filteredCollections
},
},
methods: {
displayModalAdd(shouldDisplay) {
this.showModalAdd = shouldDisplay
},
displayModalEdit(shouldDisplay) {
this.showModalEdit = shouldDisplay
if (!shouldDisplay) this.resetSelectedData()
},
displayModalImportExport(shouldDisplay) {
this.showModalImportExport = shouldDisplay
},
displayModalAddRequest(shouldDisplay) {
this.showModalAddRequest = shouldDisplay
if (!shouldDisplay) this.resetSelectedData()
},
displayModalAddFolder(shouldDisplay) {
this.showModalAddFolder = shouldDisplay
if (!shouldDisplay) this.resetSelectedData()
},
displayModalEditFolder(shouldDisplay) {
this.showModalEditFolder = shouldDisplay
if (!shouldDisplay) this.resetSelectedData()
},
displayModalEditRequest(shouldDisplay) {
this.showModalEditRequest = shouldDisplay
if (!shouldDisplay) this.resetSelectedData()
},
editCollection(collection, collectionIndex) {
this.$data.editingCollection = collection
this.$data.editingCollectionIndex = collectionIndex
this.displayModalEdit(true)
},
onAddRequest({ name, path, index }) {
const newRequest = {
...this.tabs.currentActiveTab.value.document.request,
name,
}
saveGraphqlRequestAs(path, newRequest)
this.tabs.createNewTab({
saveContext: {
originLocation: "user-collection",
folderPath: path,
requestIndex: index,
},
request: newRequest,
isDirty: false,
})
platform.analytics?.logEvent({
type: "HOPP_SAVE_REQUEST",
platform: "gql",
createdNow: true,
workspaceType: "personal",
})
this.displayModalAddRequest(false)
},
addRequest(payload) {
const { path } = payload
this.$data.editingFolderPath = path
this.displayModalAddRequest(true)
},
onAddFolder({ name, path }) {
addGraphqlFolder(name, path)
platform.analytics?.logEvent({
type: "HOPP_CREATE_COLLECTION",
isRootCollection: false,
platform: "gql",
workspaceType: "personal",
})
this.displayModalAddFolder(false)
},
addFolder(payload) {
const { path } = payload
this.$data.editingFolderPath = path
this.displayModalAddFolder(true)
},
editFolder(payload) {
const { folder, folderPath } = payload
this.editingFolder = folder
this.editingFolderPath = folderPath
this.displayModalEditFolder(true)
},
editRequest(payload) {
const {
collectionIndex,
folderIndex,
folderName,
request,
requestIndex,
folderPath,
} = payload
this.$data.editingFolderPath = folderPath
this.$data.editingCollectionIndex = collectionIndex
this.$data.editingFolderIndex = folderIndex
this.$data.editingFolderName = folderName
this.$data.editingRequest = request
this.$data.editingRequestIndex = requestIndex
this.displayModalEditRequest(true)
},
resetSelectedData() {
this.$data.editingCollection = undefined
this.$data.editingCollectionIndex = undefined
this.$data.editingFolder = undefined
this.$data.editingFolderIndex = undefined
this.$data.editingRequest = undefined
this.$data.editingRequestIndex = undefined
},
duplicateRequest({ folderPath, request }) {
saveGraphqlRequestAs(folderPath, {
...cloneDeep(request),
name: `${request.name} - ${this.t("action.duplicate")}`,
})
},
},
const editingProperties = ref<{
collection: HoppCollection | null
isRootCollection: boolean
path: string
inheritedProperties?: HoppInheritedProperty
}>({
collection: null,
isRootCollection: false,
path: "",
inheritedProperties: undefined,
})
const filterText = ref("")
const filteredCollections = computed(() => {
const collectionsClone = clone(collections.value)
if (!filterText.value) return collectionsClone
const filterTextLower = filterText.value.toLowerCase()
const filteredCollections = []
for (const collection of collectionsClone) {
const filteredRequests = []
const filteredFolders = []
for (const request of collection.requests) {
if (request.name.toLowerCase().includes(filterTextLower))
filteredRequests.push(request)
}
for (const folder of collection.folders) {
const filteredFolderRequests = []
for (const request of folder.requests) {
if (request.name.toLowerCase().includes(filterTextLower))
filteredFolderRequests.push(request)
}
if (filteredFolderRequests.length > 0) {
const filteredFolder = { ...folder }
filteredFolder.requests = filteredFolderRequests
filteredFolders.push(filteredFolder)
}
}
if (filteredRequests.length + filteredFolders.length > 0) {
const filteredCollection = { ...collection }
filteredCollection.requests = filteredRequests
filteredCollection.folders = filteredFolders
filteredCollections.push(filteredCollection)
}
}
return filteredCollections
})
const displayModalAdd = (shouldDisplay: boolean) => {
showModalAdd.value = shouldDisplay
}
const displayModalEdit = (shouldDisplay: boolean) => {
showModalEdit.value = shouldDisplay
if (!shouldDisplay) resetSelectedData()
}
const displayModalImportExport = (shouldDisplay: boolean) => {
showModalImportExport.value = shouldDisplay
}
const displayModalAddRequest = (shouldDisplay: boolean) => {
showModalAddRequest.value = shouldDisplay
if (!shouldDisplay) resetSelectedData()
}
const displayModalAddFolder = (shouldDisplay: boolean) => {
showModalAddFolder.value = shouldDisplay
if (!shouldDisplay) resetSelectedData()
}
const displayModalEditFolder = (shouldDisplay: boolean) => {
showModalEditFolder.value = shouldDisplay
if (!shouldDisplay) resetSelectedData()
}
const displayModalEditRequest = (shouldDisplay: boolean) => {
showModalEditRequest.value = shouldDisplay
if (!shouldDisplay) resetSelectedData()
}
const displayModalEditProperties = (show: boolean) => {
showModalEditProperties.value = show
if (!show) resetSelectedData()
}
const editCollection = (
collection: HoppCollection,
collectionIndex: number
) => {
editingCollection.value = collection
editingCollectionIndex.value = collectionIndex
displayModalEdit(true)
}
const onAddRequest = ({
name,
path,
index,
}: {
name: string
path: string
index: number
}) => {
const newRequest = {
...tabs.currentActiveTab.value.document.request,
name,
}
saveGraphqlRequestAs(path, newRequest)
const { auth, headers } = cascadeParentCollectionForHeaderAuth(
path,
"graphql"
)
tabs.createNewTab({
saveContext: {
originLocation: "user-collection",
folderPath: path,
requestIndex: index,
},
request: newRequest,
isDirty: false,
inheritedProperties: {
auth,
headers,
},
})
platform.analytics?.logEvent({
type: "HOPP_SAVE_REQUEST",
platform: "gql",
createdNow: true,
workspaceType: "personal",
})
displayModalAddRequest(false)
}
const addRequest = (payload: { path: string }) => {
const { path } = payload
editingFolderPath.value = path
displayModalAddRequest(true)
}
const onAddFolder = ({
name,
path,
}: {
name: string
path: string | undefined
}) => {
addGraphqlFolder(name, path ?? "0")
platform.analytics?.logEvent({
type: "HOPP_CREATE_COLLECTION",
isRootCollection: false,
platform: "gql",
workspaceType: "personal",
})
displayModalAddFolder(false)
}
const addFolder = (payload: { path: string }) => {
const { path } = payload
editingFolderPath.value = path
displayModalAddFolder(true)
}
const editFolder = (payload: {
folder: HoppCollection
folderPath: string
}) => {
const { folder, folderPath } = payload
editingFolder.value = folder
editingFolderPath.value = folderPath
displayModalEditFolder(true)
}
const editRequest = (payload: {
collectionIndex: number
folderIndex: number
folderName: string
request: HoppGQLRequest
requestIndex: number
folderPath: string
}) => {
const {
collectionIndex,
folderIndex,
folderName,
request,
requestIndex,
folderPath,
} = payload
editingFolderPath.value = folderPath
editingCollectionIndex.value = collectionIndex
editingFolderIndex.value = folderIndex
editingFolderName.value = folderName
editingRequest.value = request
editingRequestIndex.value = requestIndex
displayModalEditRequest(true)
}
const duplicateRequest = ({
folderPath,
request,
}: {
folderPath: string
request: HoppGQLRequest
}) => {
saveGraphqlRequestAs(folderPath, {
...cloneDeep(request),
name: `${request.name} - ${t("action.duplicate")}`,
})
}
const selectRequest = ({
request,
folderPath,
requestIndex,
}: {
request: HoppGQLRequest
folderPath: string
requestIndex: number
}) => {
const possibleTab = tabs.getTabRefWithSaveContext({
originLocation: "user-collection",
folderPath: folderPath,
requestIndex: requestIndex,
})
const { auth, headers } = cascadeParentCollectionForHeaderAuth(
folderPath,
"graphql"
)
// Switch to that request if that request is open
if (possibleTab) {
tabs.setActiveTab(possibleTab.value.id)
return
}
tabs.createNewTab({
saveContext: {
originLocation: "user-collection",
folderPath: folderPath,
requestIndex: requestIndex,
},
request: cloneDeep(
makeGQLRequest({
name: request.name,
url: request.url,
query: request.query,
headers: request.headers,
variables: request.variables,
auth: request.auth,
})
),
isDirty: false,
inheritedProperties: {
auth,
headers,
},
})
}
const dropRequest = ({
folderPath,
requestIndex,
collectionIndex,
}: {
folderPath: string
requestIndex: number
collectionIndex: number
}) => {
const { auth, headers } = cascadeParentCollectionForHeaderAuth(
`${collectionIndex}`,
"graphql"
)
const possibleTab = tabs.getTabRefWithSaveContext({
originLocation: "user-collection",
folderPath,
requestIndex: Number(requestIndex),
})
if (possibleTab) {
possibleTab.value.document.saveContext = {
originLocation: "user-collection",
folderPath: `${collectionIndex}`,
requestIndex: getRequestsByPath(collections.value, `${collectionIndex}`)
.length,
}
possibleTab.value.document.inheritedProperties = {
auth,
headers,
}
}
moveGraphqlRequest(folderPath, requestIndex, `${collectionIndex}`)
toast.success(`${t("request.moved")}`)
}
/**
* Checks if the collection is already in the root
* @param id - path of the collection
* @returns boolean - true if the collection is already in the root
*/
const isAlreadyInRoot = (id: string) => {
const indexPath = id.split("/")
return indexPath.length === 1
}
const editProperties = ({
collectionIndex,
collection,
}: {
collectionIndex: string | null
collection: HoppCollection | null
}) => {
if (collectionIndex === null || collection === null) return
const parentIndex = collectionIndex.split("/").slice(0, -1).join("/") // remove last folder to get parent folder
let inheritedProperties = {}
if (parentIndex) {
const { auth, headers } = cascadeParentCollectionForHeaderAuth(
parentIndex,
"graphql"
)
inheritedProperties = {
auth,
headers,
} as HoppInheritedProperty
}
editingProperties.value = {
collection,
isRootCollection: isAlreadyInRoot(collectionIndex),
path: collectionIndex,
inheritedProperties,
}
displayModalEditProperties(true)
}
const setCollectionProperties = (newCollection: {
collection: HoppCollection
path: string
isRootCollection: boolean
}) => {
const { collection, path, isRootCollection } = newCollection
if (isRootCollection) {
editGraphqlCollection(parseInt(path), collection)
} else {
editGraphqlFolder(path, collection)
}
const { auth, headers } = cascadeParentCollectionForHeaderAuth(
path,
"graphql"
)
nextTick(() => {
updateInheritedPropertiesForAffectedRequests(
path,
{
auth,
headers,
},
"graphql"
)
})
displayModalEditProperties(false)
}
const resetSelectedData = () => {
editingCollection.value = null
editingCollectionIndex.value = null
editingFolder.value = null
editingFolderIndex.value = null
editingRequest.value = null
editingRequestIndex.value = null
}
</script>