Compare commits

..

3 Commits

15 changed files with 188 additions and 304 deletions

View File

@@ -1,48 +0,0 @@
# THIS IS NOT TO BE USED FOR PERSONAL DEPLOYMENTS!
# Internal Docker Compose Image used for internal testing deployments
version: "3.7"
services:
hoppscotch-db:
image: postgres:15
user: postgres
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: testpass
POSTGRES_DB: hoppscotch
healthcheck:
test:
[
"CMD-SHELL",
"sh -c 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}'"
]
interval: 5s
timeout: 5s
retries: 10
hoppscotch-aio:
container_name: hoppscotch-aio
build:
dockerfile: prod.Dockerfile
context: .
target: aio
environment:
- DATABASE_URL=postgresql://postgres:testpass@hoppscotch-db:5432/hoppscotch
- ENABLE_SUBPATH_BASED_ACCESS=true
env_file:
- ./.env
depends_on:
hoppscotch-db:
condition: service_healthy
command: ["sh", "-c", "pnpm exec prisma migrate deploy && node /usr/src/app/aio_run.mjs"]
healthcheck:
test:
- CMD
- curl
- '-f'
- 'http://localhost:80'
interval: 2s
timeout: 10s
retries: 30

View File

@@ -1,22 +1,17 @@
-- This is a custom migration file which is not generated by Prisma. -- AlterTable
-- The aim of this migration is to add text search indices to the TeamCollection and TeamRequest tables. ALTER TABLE
-- Create Extension
CREATE EXTENSION IF NOT EXISTS pg_trgm;
-- Create GIN Trigram Index for Team Collection title
CREATE INDEX
"TeamCollection_title_trgm_idx"
ON
"TeamCollection" "TeamCollection"
USING ADD
GIN (title gin_trgm_ops); titleSearch tsvector GENERATED ALWAYS AS (to_tsvector('english', title)) STORED;
-- Create GIN Trigram Index for Team Collection title -- AlterTable
CREATE INDEX ALTER TABLE
"TeamRequest_title_trgm_idx"
ON
"TeamRequest" "TeamRequest"
USING ADD
GIN (title gin_trgm_ops); titleSearch tsvector GENERATED ALWAYS AS (to_tsvector('english', title)) STORED;
-- CreateIndex
CREATE INDEX "TeamCollection_textSearch_idx" ON "TeamCollection" USING GIN (titleSearch);
-- CreateIndex
CREATE INDEX "TeamRequest_textSearch_idx" ON "TeamRequest" USING GIN (titleSearch);

View File

@@ -20,7 +20,7 @@ import {
TEAM_COLL_PARENT_TREE_GEN_FAILED, TEAM_COLL_PARENT_TREE_GEN_FAILED,
} from '../errors'; } from '../errors';
import { PubSubService } from '../pubsub/pubsub.service'; import { PubSubService } from '../pubsub/pubsub.service';
import { escapeSqlLikeString, isValidLength } from 'src/utils'; import { isValidLength } from 'src/utils';
import * as E from 'fp-ts/Either'; import * as E from 'fp-ts/Either';
import * as O from 'fp-ts/Option'; import * as O from 'fp-ts/Option';
import { Prisma, TeamCollection as DBTeamCollection } from '@prisma/client'; import { Prisma, TeamCollection as DBTeamCollection } from '@prisma/client';
@@ -1125,7 +1125,7 @@ export class TeamCollectionService {
id: searchResults[i].id, id: searchResults[i].id,
path: !fetchedParentTree path: !fetchedParentTree
? [] ? []
: (fetchedParentTree.right as CollectionSearchNode[]), : ([fetchedParentTree.right] as CollectionSearchNode[]),
}); });
} }
@@ -1148,20 +1148,14 @@ export class TeamCollectionService {
skip: number, skip: number,
) { ) {
const query = Prisma.sql` const query = Prisma.sql`
SELECT select id,title,'collection' AS type
id,title,'collection' AS type from "TeamCollection"
FROM where "TeamCollection"."teamID"=${teamID}
"TeamCollection" and titlesearch @@ to_tsquery(${searchQuery})
WHERE order by ts_rank(titlesearch,to_tsquery(${searchQuery}))
"TeamCollection"."teamID"=${teamID} limit ${take}
AND
title ILIKE ${`%${escapeSqlLikeString(searchQuery)}%`}
ORDER BY
similarity(title, ${searchQuery})
LIMIT ${take}
OFFSET ${skip === 0 ? 0 : (skip - 1) * take}; OFFSET ${skip === 0 ? 0 : (skip - 1) * take};
`; `;
try { try {
const res = await this.prisma.$queryRaw<SearchQueryReturnType[]>(query); const res = await this.prisma.$queryRaw<SearchQueryReturnType[]>(query);
return E.right(res); return E.right(res);
@@ -1186,17 +1180,12 @@ export class TeamCollectionService {
skip: number, skip: number,
) { ) {
const query = Prisma.sql` const query = Prisma.sql`
SELECT select id,title,request->>'method' as method,'request' AS type
id,title,request->>'method' as method,'request' AS type from "TeamRequest"
FROM where "TeamRequest"."teamID"=${teamID}
"TeamRequest" and titlesearch @@ to_tsquery(${searchQuery})
WHERE order by ts_rank(titlesearch,to_tsquery(${searchQuery}))
"TeamRequest"."teamID"=${teamID} limit ${take}
AND
title ILIKE ${`%${escapeSqlLikeString(searchQuery)}%`}
ORDER BY
similarity(title, ${searchQuery})
LIMIT ${take}
OFFSET ${skip === 0 ? 0 : (skip - 1) * take}; OFFSET ${skip === 0 ? 0 : (skip - 1) * take};
`; `;
@@ -1261,53 +1250,45 @@ export class TeamCollectionService {
* @returns The parent tree of the parent collections * @returns The parent tree of the parent collections
*/ */
private generateParentTree(parentCollections: ParentTreeQueryReturnType[]) { private generateParentTree(parentCollections: ParentTreeQueryReturnType[]) {
function findChildren(id: string): CollectionSearchNode[] { function findChildren(id) {
const collection = parentCollections.filter((item) => item.id === id)[0]; const collection = parentCollections.filter((item) => item.id === id)[0];
if (collection.parentID == null) { if (collection.parentID == null) {
return <CollectionSearchNode[]>[ return {
{
id: collection.id,
title: collection.title,
type: 'collection' as const,
path: [],
},
];
}
const res = <CollectionSearchNode[]>[
{
id: collection.id, id: collection.id,
title: collection.title, title: collection.title,
type: 'collection' as const, type: 'collection',
path: findChildren(collection.parentID), path: [],
}, };
]; }
const res = {
id: collection.id,
title: collection.title,
type: 'collection',
path: findChildren(collection.parentID),
};
return res; return res;
} }
if (parentCollections.length > 0) { if (parentCollections.length > 0) {
if (parentCollections[0].parentID == null) { if (parentCollections[0].parentID == null) {
return <CollectionSearchNode[]>[ return {
{
id: parentCollections[0].id,
title: parentCollections[0].title,
type: 'collection',
path: [],
},
];
}
return <CollectionSearchNode[]>[
{
id: parentCollections[0].id, id: parentCollections[0].id,
title: parentCollections[0].title, title: parentCollections[0].title,
type: 'collection', type: 'collection',
path: findChildren(parentCollections[0].parentID), path: [],
}, };
]; }
return {
id: parentCollections[0].id,
title: parentCollections[0].title,
type: 'collection',
path: findChildren(parentCollections[0].parentID),
};
} }
return <CollectionSearchNode[]>[]; return null;
} }
/** /**

View File

@@ -250,40 +250,3 @@ export function checkEnvironmentAuthProvider(
} }
} }
} }
/**
* Adds escape backslashes to the input so that it can be used inside
* SQL LIKE/ILIKE queries. Inspired by PHP's `mysql_real_escape_string`
* function.
*
* Eg. "100%" -> "100\\%"
*
* Source: https://stackoverflow.com/a/32648526
*/
export function escapeSqlLikeString(str: string) {
if (typeof str != 'string')
return str;
return str.replace(/[\0\x08\x09\x1a\n\r"'\\\%]/g, function (char) {
switch (char) {
case "\0":
return "\\0";
case "\x08":
return "\\b";
case "\x09":
return "\\t";
case "\x1a":
return "\\z";
case "\n":
return "\\n";
case "\r":
return "\\r";
case "\"":
case "'":
case "\\":
case "%":
return "\\"+char; // prepends a backslash to backslash, percent,
// and double/single quotes
}
});
}

View File

@@ -131,7 +131,7 @@ const getCollectionStack = (collections: HoppCollection[]): CollectionStack[] =>
* path of each request within collection-json file, failed-tests-report, errors, * path of each request within collection-json file, failed-tests-report, errors,
* total execution duration for requests, pre-request-scripts, test-scripts. * total execution duration for requests, pre-request-scripts, test-scripts.
* @returns True, if collection runner executed without any errors or failed test-cases. * @returns True, if collection runner executed without any errors or failed test-cases.
* False, if errors occurred or test-cases failed. * False, if errors occured or test-cases failed.
*/ */
export const collectionsRunnerResult = ( export const collectionsRunnerResult = (
requestsReport: RequestReport[] requestsReport: RequestReport[]

View File

@@ -112,7 +112,7 @@ export const printTestsMetrics = (testsMetrics: TestMetrics) => {
/** /**
* Prints details of each reported error for a request with error code. * Prints details of each reported error for a request with error code.
* @param path Request's path in collection for which errors occurred. * @param path Request's path in collection for which errors occured.
* @param errorsReport List of errors reported. * @param errorsReport List of errors reported.
*/ */
export const printErrorsReport = ( export const printErrorsReport = (

View File

@@ -66,18 +66,19 @@
<script setup lang="ts"> <script setup lang="ts">
import { watch, ref } from "vue" import { watch, ref } from "vue"
import { useI18n } from "@composables/i18n" import { useI18n } from "@composables/i18n"
import { HoppCollection, HoppRESTAuth, HoppRESTHeaders } from "@hoppscotch/data" import { HoppCollection } from "@hoppscotch/data"
import { RESTOptionTabs } from "../http/RequestOptions.vue" import { RESTOptionTabs } from "../http/RequestOptions.vue"
import { TeamCollection } from "~/helpers/teams/TeamCollection"
import { clone } from "lodash-es" import { clone } from "lodash-es"
import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties" import { HoppInheritedProperty } from "~/helpers/types/HoppInheritedProperties"
const t = useI18n() const t = useI18n()
type EditingProperties = { type EditingProperties = {
collection: Partial<HoppCollection> | null collection: HoppCollection | TeamCollection | null
isRootCollection: boolean isRootCollection: boolean
path: string path: string
inheritedProperties?: HoppInheritedProperty inheritedProperties: HoppInheritedProperty | undefined
} }
const props = withDefaults( const props = withDefaults(
@@ -94,23 +95,21 @@ const props = withDefaults(
) )
const emit = defineEmits<{ const emit = defineEmits<{
( (e: "set-collection-properties", newCollection: any): void
e: "set-collection-properties",
newCollection: Omit<EditingProperties, "inheritedProperties">
): void
(e: "hide-modal"): void (e: "hide-modal"): void
}>() }>()
const editableCollection = ref<{ const editableCollection = ref({
headers: HoppRESTHeaders body: {
auth: HoppRESTAuth contentType: null,
}>({ body: null,
},
headers: [], headers: [],
auth: { auth: {
authType: "inherit", authType: "inherit",
authActive: false, authActive: false,
}, },
}) }) as any
const selectedOptionTab = ref("headers") const selectedOptionTab = ref("headers")
@@ -123,13 +122,17 @@ watch(
(show) => { (show) => {
if (show && props.editingProperties?.collection) { if (show && props.editingProperties?.collection) {
editableCollection.value.auth = clone( editableCollection.value.auth = clone(
props.editingProperties.collection.auth as HoppRESTAuth props.editingProperties.collection.auth
) )
editableCollection.value.headers = clone( editableCollection.value.headers = clone(
props.editingProperties.collection.headers as HoppRESTHeaders props.editingProperties.collection.headers
) )
} else { } else {
editableCollection.value = { editableCollection.value = {
body: {
contentType: null,
body: null,
},
headers: [], headers: [],
auth: { auth: {
authType: "inherit", authType: "inherit",
@@ -143,6 +146,7 @@ watch(
const saveEditedCollection = () => { const saveEditedCollection = () => {
if (!props.editingProperties) return if (!props.editingProperties) return
const finalCollection = clone(editableCollection.value) const finalCollection = clone(editableCollection.value)
delete finalCollection.body
const collection = { const collection = {
path: props.editingProperties.path, path: props.editingProperties.path,
collection: { collection: {
@@ -151,7 +155,7 @@ const saveEditedCollection = () => {
}, },
isRootCollection: props.editingProperties.isRootCollection, isRootCollection: props.editingProperties.isRootCollection,
} }
emit("set-collection-properties", collection as EditingProperties) emit("set-collection-properties", collection)
} }
const hideModal = () => { const hideModal = () => {

View File

@@ -292,7 +292,7 @@ const editingRequestIndex = ref<number | null>(null)
const editingRequestID = ref<string | null>(null) const editingRequestID = ref<string | null>(null)
const editingProperties = ref<{ const editingProperties = ref<{
collection: Partial<HoppCollection> | null collection: Omit<HoppCollection, "v"> | TeamCollection | null
isRootCollection: boolean isRootCollection: boolean
path: string path: string
inheritedProperties?: HoppInheritedProperty inheritedProperties?: HoppInheritedProperty
@@ -739,7 +739,7 @@ const onAddRequest = (requestName: string) => {
saveContext: { saveContext: {
originLocation: "team-collection", originLocation: "team-collection",
requestID: createRequestInCollection.id, requestID: createRequestInCollection.id,
collectionID: path, collectionID: createRequestInCollection.collection.id,
teamID: createRequestInCollection.collection.team.id, teamID: createRequestInCollection.collection.team.id,
}, },
inheritedProperties: { inheritedProperties: {
@@ -2021,7 +2021,7 @@ const editProperties = (payload: {
{ {
parentID: "", parentID: "",
parentName: "", parentName: "",
inheritedHeader: {}, inheritedHeaders: [],
}, },
], ],
} as HoppInheritedProperty } as HoppInheritedProperty
@@ -2039,7 +2039,7 @@ const editProperties = (payload: {
} }
editingProperties.value = { editingProperties.value = {
collection: collection as Partial<HoppCollection>, collection,
isRootCollection: isAlreadyInRoot(collectionIndex), isRootCollection: isAlreadyInRoot(collectionIndex),
path: collectionIndex, path: collectionIndex,
inheritedProperties, inheritedProperties,
@@ -2083,7 +2083,7 @@ const editProperties = (payload: {
} }
editingProperties.value = { editingProperties.value = {
collection: coll as unknown as Partial<HoppCollection>, collection: coll,
isRootCollection: isAlreadyInRoot(collectionIndex), isRootCollection: isAlreadyInRoot(collectionIndex),
path: collectionIndex, path: collectionIndex,
inheritedProperties, inheritedProperties,
@@ -2094,12 +2094,11 @@ const editProperties = (payload: {
} }
const setCollectionProperties = (newCollection: { const setCollectionProperties = (newCollection: {
collection: Partial<HoppCollection> | null collection: HoppCollection
isRootCollection: boolean
path: string path: string
isRootCollection: boolean
}) => { }) => {
const { collection, path, isRootCollection } = newCollection const { collection, path, isRootCollection } = newCollection
if (!collection) return
if (collectionsType.value.type === "my-collections") { if (collectionsType.value.type === "my-collections") {
if (isRootCollection) { if (isRootCollection) {
@@ -2149,7 +2148,8 @@ const setCollectionProperties = (newCollection: {
auth, auth,
headers, headers,
}, },
"rest" "rest",
"team"
) )
}, 200) }, 200)
} }

View File

@@ -307,7 +307,6 @@ import { useColorMode } from "@composables/theming"
import { computed, reactive, ref, watch } from "vue" import { computed, reactive, ref, watch } from "vue"
import { isEqual, cloneDeep } from "lodash-es" import { isEqual, cloneDeep } from "lodash-es"
import { import {
HoppRESTAuth,
HoppRESTHeader, HoppRESTHeader,
HoppRESTRequest, HoppRESTRequest,
parseRawKeyValueEntriesE, parseRawKeyValueEntriesE,
@@ -365,12 +364,7 @@ const deletionToast = ref<{ goAway: (delay: number) => void } | null>(null)
// v-model integration with props and emit // v-model integration with props and emit
const props = defineProps<{ const props = defineProps<{
modelValue: modelValue: HoppRESTRequest
| HoppRESTRequest
| {
headers: HoppRESTHeader[]
auth: HoppRESTAuth
}
isCollectionProperty?: boolean isCollectionProperty?: boolean
inheritedProperties?: HoppInheritedProperty inheritedProperties?: HoppInheritedProperty
envs?: AggregateEnvironment[] envs?: AggregateEnvironment[]

View File

@@ -39,7 +39,7 @@ export function onLoggedIn(exec: (user: HoppUser) => void) {
* the auth system. * the auth system.
* *
* NOTE: Unlike `onLoggedIn` for which the callback will be called once on mount with the current state, * NOTE: Unlike `onLoggedIn` for which the callback will be called once on mount with the current state,
* here the callback will only be called on authentication event occurrences. * here the callback will only be called on authentication event occurances.
* You might want to check the auth state from an `onMounted` hook or something * You might want to check the auth state from an `onMounted` hook or something
* if you want to access the initial state * if you want to access the initial state
* *

View File

@@ -109,6 +109,7 @@ export function updateSaveContextForAffectedRequests(
} }
} }
} }
/** /**
* Used to check the new folder path is close to the save context folder path or not * 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 folderPathCurrent The path saved as the inherited path in the inherited properties
@@ -122,109 +123,120 @@ function folderPathCloseToSaveContext(
saveContextPath: string saveContextPath: string
) { ) {
if (!folderPathCurrent) return newFolderPath if (!folderPathCurrent) return newFolderPath
const folderPathCurrentArray = folderPathCurrent.split("/") const folderPathCurrentArray = folderPathCurrent.split("/")
const newFolderPathArray = newFolderPath.split("/") const newFolderPathArray = newFolderPath.split("/")
const saveContextFolderPathArray = saveContextPath.split("/") const saveContextFolderPathArray = saveContextPath.split("/")
const folderPathCurrentMatch = folderPathCurrentArray.filter( let folderPathCurrentMatch = 0
(folder, i) => folder === saveContextFolderPathArray[i]
).length
const newFolderPathMatch = newFolderPathArray.filter( for (let i = 0; i < folderPathCurrentArray.length; i++) {
(folder, i) => folder === saveContextFolderPathArray[i] if (folderPathCurrentArray[i] === saveContextFolderPathArray[i]) {
).length folderPathCurrentMatch++
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 let newFolderPathMatch = 0
result.sort((a, b) => a.parentID.localeCompare(b.parentID))
return result for (let i = 0; i < newFolderPathArray.length; i++) {
if (newFolderPathArray[i] === saveContextFolderPathArray[i]) {
newFolderPathMatch++
}
}
if (folderPathCurrentMatch > newFolderPathMatch) {
return folderPathCurrent
}
return newFolderPath
} }
export function updateInheritedPropertiesForAffectedRequests( export function updateInheritedPropertiesForAffectedRequests(
path: string, path: string,
inheritedProperties: HoppInheritedProperty, inheritedProperties: HoppInheritedProperty,
type: "rest" | "graphql" type: "rest" | "graphql",
workspace: "personal" | "team" = "personal"
) { ) {
const tabService = const tabService =
type === "rest" ? getService(RESTTabService) : getService(GQLTabService) type === "rest" ? getService(RESTTabService) : getService(GQLTabService)
const effectedTabs = tabService.getTabsRefTo((tab) => { let tabs
const saveContext = tab.document.saveContext if (workspace === "personal") {
tabs = tabService.getTabsRefTo((tab) => {
return (
tab.document.saveContext?.originLocation === "user-collection" &&
tab.document.saveContext.folderPath.startsWith(path)
)
})
} else {
tabs = tabService.getTabsRefTo((tab) => {
return (
tab.document.saveContext?.originLocation === "team-collection" &&
tab.document.saveContext.collectionID?.startsWith(path)
)
})
}
const saveContextPath = const tabsEffectedByAuth = tabs.filter((tab) => {
saveContext?.originLocation === "team-collection" if (workspace === "personal") {
? saveContext.collectionID return (
: saveContext?.folderPath tab.value.document.saveContext?.originLocation === "user-collection" &&
tab.value.document.saveContext.folderPath.startsWith(path) &&
return saveContextPath?.startsWith(path) ?? false path ===
}) folderPathCloseToSaveContext(
tab.value.document.inheritedProperties?.auth.parentID,
effectedTabs.map((tab) => { path,
const inheritedParentID = tab.value.document.saveContext.folderPath
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) { return (
// filter out the headers with the parentID not as the path tab.value.document.saveContext?.originLocation === "team-collection" &&
const headers = tab.value.document.inheritedProperties.headers.filter( tab.value.document.saveContext.collectionID?.startsWith(path) &&
(header) => header.parentID !== path path ===
) folderPathCloseToSaveContext(
tab.value.document.inheritedProperties?.auth.parentID,
path,
tab.value.document.saveContext.collectionID
)
)
})
// filter out the headers with the parentID as the path in the inheritedProperties const tabsEffectedByHeaders = tabs.filter((tab) => {
const inheritedHeaders = inheritedProperties.headers.filter( return (
tab.value.document.inheritedProperties &&
tab.value.document.inheritedProperties.headers.some(
(header) => header.parentID === path (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
}
}) })
for (const tab of tabsEffectedByAuth) {
tab.value.document.inheritedProperties = inheritedProperties
}
for (const tab of tabsEffectedByHeaders) {
const headers = tab.value.document.inheritedProperties?.headers.map(
(header) => {
if (header.parentID === path) {
return {
...header,
inheritedHeader: inheritedProperties.headers.find(
(inheritedHeader) =>
inheritedHeader.inheritedHeader?.key ===
header.inheritedHeader?.key
)?.inheritedHeader,
}
}
return header
}
)
tab.value.document.inheritedProperties = {
...tab.value.document.inheritedProperties,
headers,
}
}
} }
function resetSaveContextForAffectedRequests(folderPath: string) { function resetSaveContextForAffectedRequests(folderPath: string) {

View File

@@ -1,10 +1,10 @@
/** /**
* Converts an array of key-value tuples (for e.g ["key", "value"]), into a record. * Converts an array of key-value tuples (for e.g ["key", "value"]), into a record.
* (for eg. output -> { "key": "value" }) * (for eg. output -> { "key": "value" })
* NOTE: This function will discard duplicate key occurrences and only keep the last occurrence. If you do not want that behaviour, * NOTE: This function will discard duplicate key occurances and only keep the last occurance. If you do not want that behaviour,
* use `tupleWithSamesKeysToRecord`. * use `tupleWithSamesKeysToRecord`.
* @param tuples Array of tuples ([key, value]) * @param tuples Array of tuples ([key, value])
* @returns A record with value corresponding to the last occurrence of that key * @returns A record with value corresponding to the last occurance of that key
*/ */
export const tupleToRecord = < export const tupleToRecord = <
KeyType extends string | number | symbol, KeyType extends string | number | symbol,

View File

@@ -18,8 +18,6 @@ import {
HoppRESTParam, HoppRESTParam,
parseRawKeyValueEntriesE, parseRawKeyValueEntriesE,
parseTemplateStringE, parseTemplateStringE,
HoppRESTAuth,
HoppRESTHeaders,
} from "@hoppscotch/data" } from "@hoppscotch/data"
import { arrayFlatMap, arraySort } from "../functional/array" import { arrayFlatMap, arraySort } from "../functional/array"
import { toFormData } from "../functional/formData" import { toFormData } from "../functional/formData"
@@ -46,12 +44,7 @@ export interface EffectiveHoppRESTRequest extends HoppRESTRequest {
*/ */
export const getComputedAuthHeaders = ( export const getComputedAuthHeaders = (
envVars: Environment["variables"], envVars: Environment["variables"],
req?: req?: HoppRESTRequest,
| HoppRESTRequest
| {
auth: HoppRESTAuth
headers: HoppRESTHeaders
},
auth?: HoppRESTRequest["auth"], auth?: HoppRESTRequest["auth"],
parse = true parse = true
) => { ) => {
@@ -115,12 +108,7 @@ export const getComputedAuthHeaders = (
* @returns The list of headers * @returns The list of headers
*/ */
export const getComputedBodyHeaders = ( export const getComputedBodyHeaders = (
req: req: HoppRESTRequest
| HoppRESTRequest
| {
auth: HoppRESTAuth
headers: HoppRESTHeaders
}
): HoppRESTHeader[] => { ): HoppRESTHeader[] => {
// If a content-type is already defined, that will override this // If a content-type is already defined, that will override this
if ( if (
@@ -130,10 +118,8 @@ export const getComputedBodyHeaders = (
) )
return [] return []
if (!("body" in req)) return []
// Body should have a non-null content-type // Body should have a non-null content-type
if (!req.body || req.body.contentType === null) return [] if (req.body.contentType === null) return []
return [ return [
{ {
@@ -157,12 +143,7 @@ export type ComputedHeader = {
* @returns The headers that are generated along with the source of that header * @returns The headers that are generated along with the source of that header
*/ */
export const getComputedHeaders = ( export const getComputedHeaders = (
req: req: HoppRESTRequest,
| HoppRESTRequest
| {
auth: HoppRESTAuth
headers: HoppRESTHeaders
},
envVars: Environment["variables"], envVars: Environment["variables"],
parse = true parse = true
): ComputedHeader[] => { ): ComputedHeader[] => {

View File

@@ -17,7 +17,7 @@
"@fontsource-variable/material-symbols-rounded": "5.0.5", "@fontsource-variable/material-symbols-rounded": "5.0.5",
"@fontsource-variable/roboto-mono": "5.0.6", "@fontsource-variable/roboto-mono": "5.0.6",
"@graphql-typed-document-node/core": "3.1.1", "@graphql-typed-document-node/core": "3.1.1",
"@hoppscotch/ui": "0.1.3", "@hoppscotch/ui": "0.1.2",
"@hoppscotch/vue-toasted": "0.1.0", "@hoppscotch/vue-toasted": "0.1.0",
"@intlify/unplugin-vue-i18n": "1.2.0", "@intlify/unplugin-vue-i18n": "1.2.0",
"@types/cors": "2.8.13", "@types/cors": "2.8.13",

10
pnpm-lock.yaml generated
View File

@@ -1258,8 +1258,8 @@ importers:
specifier: 3.1.1 specifier: 3.1.1
version: 3.1.1(graphql@16.6.0) version: 3.1.1(graphql@16.6.0)
'@hoppscotch/ui': '@hoppscotch/ui':
specifier: 0.1.3 specifier: 0.1.2
version: 0.1.3(eslint@8.55.0)(terser@5.27.0)(vite@3.2.4)(vue@3.3.9) version: 0.1.2(eslint@8.55.0)(terser@5.27.0)(vite@3.2.4)(vue@3.3.9)
'@hoppscotch/vue-toasted': '@hoppscotch/vue-toasted':
specifier: 0.1.0 specifier: 0.1.0
version: 0.1.0(vue@3.3.9) version: 0.1.0(vue@3.3.9)
@@ -6050,8 +6050,8 @@ packages:
- vite - vite
dev: false dev: false
/@hoppscotch/ui@0.1.3(eslint@8.55.0)(terser@5.27.0)(vite@3.2.4)(vue@3.3.9): /@hoppscotch/ui@0.1.2(eslint@8.55.0)(terser@5.27.0)(vite@3.2.4)(vue@3.3.9):
resolution: {integrity: sha512-a1dmqqL+zS2P6cxkCBLdBtd+mD+MnCDSN63TrCPldW5W92rtqpeZ0bmGgiQlzfA2457JRktYpVCBR0Oc0J1jbA==} resolution: {integrity: sha512-bBn7Km1iIFMBsXgnrDLqTBzz29XOPRZcRbQd18DZMYxoR7WQo9amBfa850vk5debYQx2+Mb0ExOnrGVO1QlBRg==}
engines: {node: '>=16'} engines: {node: '>=16'}
peerDependencies: peerDependencies:
vue: 3.3.9 vue: 3.3.9
@@ -24038,6 +24038,7 @@ packages:
/workbox-google-analytics@6.6.0: /workbox-google-analytics@6.6.0:
resolution: {integrity: sha512-p4DJa6OldXWd6M9zRl0H6vB9lkrmqYFkRQ2xEiNdBFp9U0LhsGO7hsBscVEyH9H2/3eZZt8c97NB2FD9U2NJ+Q==} resolution: {integrity: sha512-p4DJa6OldXWd6M9zRl0H6vB9lkrmqYFkRQ2xEiNdBFp9U0LhsGO7hsBscVEyH9H2/3eZZt8c97NB2FD9U2NJ+Q==}
deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained
dependencies: dependencies:
workbox-background-sync: 6.6.0 workbox-background-sync: 6.6.0
workbox-core: 6.6.0 workbox-core: 6.6.0
@@ -24047,6 +24048,7 @@ packages:
/workbox-google-analytics@7.0.0: /workbox-google-analytics@7.0.0:
resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==} resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==}
deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained
dependencies: dependencies:
workbox-background-sync: 7.0.0 workbox-background-sync: 7.0.0
workbox-core: 7.0.0 workbox-core: 7.0.0