feat(common): add advanced configuration for auth and token request parameters (#5253)
Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
parent
8f5eed5151
commit
b07212cb05
24 changed files with 1425 additions and 112 deletions
|
|
@ -485,17 +485,17 @@ export const WORKSPACE_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Worksp
|
|||
export const TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: HoppCollection[] =
|
||||
[
|
||||
{
|
||||
v: 8,
|
||||
v: 9,
|
||||
id: "clx1f86hv000010f8szcfya0t",
|
||||
name: "Multiple child collections with authorization & headers set at each level",
|
||||
folders: [
|
||||
{
|
||||
v: 8,
|
||||
v: 9,
|
||||
id: "clx1fjgah000110f8a5bs68gd",
|
||||
name: "folder-1",
|
||||
folders: [
|
||||
{
|
||||
v: 8,
|
||||
v: 9,
|
||||
id: "clx1fjwmm000410f8l1gkkr1a",
|
||||
name: "folder-11",
|
||||
folders: [],
|
||||
|
|
@ -537,7 +537,7 @@ export const TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Hopp
|
|||
],
|
||||
},
|
||||
{
|
||||
v: 8,
|
||||
v: 9,
|
||||
id: "clx1fjyxm000510f8pv90dt43",
|
||||
name: "folder-12",
|
||||
folders: [],
|
||||
|
|
@ -595,7 +595,7 @@ export const TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Hopp
|
|||
],
|
||||
},
|
||||
{
|
||||
v: 8,
|
||||
v: 9,
|
||||
id: "clx1fk1cv000610f88kc3aupy",
|
||||
name: "folder-13",
|
||||
folders: [],
|
||||
|
|
@ -707,12 +707,12 @@ export const TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Hopp
|
|||
],
|
||||
},
|
||||
{
|
||||
v: 8,
|
||||
v: 9,
|
||||
id: "clx1fjk9o000210f8j0573pls",
|
||||
name: "folder-2",
|
||||
folders: [
|
||||
{
|
||||
v: 8,
|
||||
v: 9,
|
||||
id: "clx1fk516000710f87sfpw6bo",
|
||||
name: "folder-21",
|
||||
folders: [],
|
||||
|
|
@ -752,7 +752,7 @@ export const TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Hopp
|
|||
],
|
||||
},
|
||||
{
|
||||
v: 8,
|
||||
v: 9,
|
||||
id: "clx1fk72t000810f8gfwkpi5y",
|
||||
name: "folder-22",
|
||||
folders: [],
|
||||
|
|
@ -810,7 +810,7 @@ export const TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Hopp
|
|||
],
|
||||
},
|
||||
{
|
||||
v: 8,
|
||||
v: 9,
|
||||
id: "clx1fk95g000910f8bunhaoo8",
|
||||
name: "folder-23",
|
||||
folders: [],
|
||||
|
|
@ -915,12 +915,12 @@ export const TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Hopp
|
|||
],
|
||||
},
|
||||
{
|
||||
v: 8,
|
||||
v: 9,
|
||||
id: "clx1fjmlq000310f86o4d3w2o",
|
||||
name: "folder-3",
|
||||
folders: [
|
||||
{
|
||||
v: 8,
|
||||
v: 9,
|
||||
id: "clx1iwq0p003e10f8u8zg0p85",
|
||||
name: "folder-31",
|
||||
folders: [],
|
||||
|
|
@ -960,7 +960,7 @@ export const TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Hopp
|
|||
],
|
||||
},
|
||||
{
|
||||
v: 8,
|
||||
v: 9,
|
||||
id: "clx1izut7003m10f894ip59zg",
|
||||
name: "folder-32",
|
||||
folders: [],
|
||||
|
|
@ -1018,7 +1018,7 @@ export const TRANSFORMED_MULTIPLE_CHILD_COLLECTIONS_WITH_AUTH_HEADERS_MOCK: Hopp
|
|||
],
|
||||
},
|
||||
{
|
||||
v: 8,
|
||||
v: 9,
|
||||
id: "clx1j2ka9003q10f8cdbzpgpg",
|
||||
name: "folder-33",
|
||||
folders: [],
|
||||
|
|
|
|||
|
|
@ -76,6 +76,71 @@ const normalizeEnvironmentVariable = (variable: HoppEnvPair): HoppEnvPair => {
|
|||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Transforms the given `HoppRESTAuth` object to ensure it conforms to the latest
|
||||
* OAuth 2.0 authentication structure. Depending on the `grantType` within the
|
||||
* `grantTypeInfo` property, this function adds or initializes specific fields
|
||||
* such as `clientAuthentication`, `authRequestParams`, `tokenRequestParams`,
|
||||
* and `refreshRequestParams` to maintain compatibility with updated schema
|
||||
* requirements.
|
||||
*
|
||||
* - For "CLIENT_CREDENTIALS" grant type, sets `clientAuthentication` to "IN_BODY"
|
||||
* and initializes `tokenRequestParams` and `refreshRequestParams` as empty arrays.
|
||||
* - For "AUTHORIZATION_CODE" grant type, initializes `authRequestParams`,
|
||||
* `tokenRequestParams`, and `refreshRequestParams` as empty arrays.
|
||||
* - For "PASSWORD" grant type, initializes `tokenRequestParams` and
|
||||
* `refreshRequestParams` as empty arrays.
|
||||
* - For "IMPLICIT" grant type, initializes `authRequestParams` and
|
||||
* `refreshRequestParams` as empty arrays.
|
||||
*
|
||||
* If the `authType` is not "oauth-2", the original `auth` object is returned unchanged.
|
||||
*
|
||||
* @param {HoppRESTAuth} auth - The authentication object to transform.
|
||||
* @returns {HoppRESTAuth} The transformed authentication object with updated grant type information.
|
||||
*/
|
||||
const transformAuth = (auth: HoppRESTAuth): HoppRESTAuth => {
|
||||
if (auth.authType === "oauth-2") {
|
||||
const oldGrantTypeInfo = auth.grantTypeInfo;
|
||||
let newGrantTypeInfo = oldGrantTypeInfo;
|
||||
|
||||
// Add clientAuthentication for CLIENT_CREDENTIALS
|
||||
if (oldGrantTypeInfo.grantType === "CLIENT_CREDENTIALS") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
clientAuthentication: "IN_BODY",
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
};
|
||||
} else if (oldGrantTypeInfo.grantType === "AUTHORIZATION_CODE") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
authRequestParams: [],
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
};
|
||||
} else if (oldGrantTypeInfo.grantType === "PASSWORD") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
};
|
||||
} else if (oldGrantTypeInfo.grantType === "IMPLICIT") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
authRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...auth,
|
||||
grantTypeInfo: newGrantTypeInfo,
|
||||
};
|
||||
}
|
||||
|
||||
return auth;
|
||||
};
|
||||
|
||||
/**
|
||||
* Transforms workspace environment data to the `HoppEnvironment` format.
|
||||
*
|
||||
|
|
@ -115,21 +180,9 @@ export const transformWorkspaceCollections = (
|
|||
const { auth = { authType: "inherit", authActive: true }, headers = [] } =
|
||||
parsedData;
|
||||
|
||||
const migratedAuth: HoppRESTAuth =
|
||||
auth.authType === "oauth-2"
|
||||
? {
|
||||
...auth,
|
||||
grantTypeInfo:
|
||||
auth.grantTypeInfo.grantType === "CLIENT_CREDENTIALS"
|
||||
? {
|
||||
...auth.grantTypeInfo,
|
||||
clientAuthentication: "IN_BODY",
|
||||
}
|
||||
: auth.grantTypeInfo,
|
||||
}
|
||||
: auth;
|
||||
const transformedAuth = transformAuth(auth);
|
||||
|
||||
const migratedHeaders = headers.map((header) =>
|
||||
const transformedHeaders = headers.map((header) =>
|
||||
header.description ? header : { ...header, description: "" }
|
||||
);
|
||||
|
||||
|
|
@ -142,8 +195,8 @@ export const transformWorkspaceCollections = (
|
|||
name: title,
|
||||
folders: transformWorkspaceCollections(folders),
|
||||
requests: transformWorkspaceRequests(requests),
|
||||
auth: migratedAuth,
|
||||
headers: migratedHeaders,
|
||||
auth: transformedAuth,
|
||||
headers: transformedHeaders,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -241,7 +241,11 @@
|
|||
"label_send_as": "Client Authentication",
|
||||
"label_send_in_body": "Send Credentials in Body",
|
||||
"label_send_as_basic_auth": "Send Credentials as Basic Auth",
|
||||
"enter_value": "Enter value"
|
||||
"enter_value": "Enter value",
|
||||
"auth_request": "Auth Request",
|
||||
"token_request": "Token Request",
|
||||
"refresh_request": "Refresh Request",
|
||||
"send_in": "Send In"
|
||||
},
|
||||
"pass_key_by": "Pass by",
|
||||
"pass_by_query_params_label": "Query Parameters",
|
||||
|
|
|
|||
|
|
@ -211,7 +211,9 @@ declare module 'vue' {
|
|||
IconLucideArrowUpRight: typeof import('~icons/lucide/arrow-up-right')['default']
|
||||
IconLucideBrush: typeof import('~icons/lucide/brush')['default']
|
||||
IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default']
|
||||
IconLucideChevronDown: typeof import('~icons/lucide/chevron-down')['default']
|
||||
IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default']
|
||||
IconLucideChevronUp: typeof import('~icons/lucide/chevron-up')['default']
|
||||
IconLucideCircleCheck: typeof import('~icons/lucide/circle-check')['default']
|
||||
IconLucideGlobe: typeof import('~icons/lucide/globe')['default']
|
||||
IconLucideHelpCircle: typeof import('~icons/lucide/help-circle')['default']
|
||||
|
|
@ -221,7 +223,6 @@ declare module 'vue' {
|
|||
IconLucideListEnd: typeof import('~icons/lucide/list-end')['default']
|
||||
IconLucideMinus: typeof import('~icons/lucide/minus')['default']
|
||||
IconLucidePlusCircle: typeof import('~icons/lucide/plus-circle')['default']
|
||||
IconLucideRss: typeof import('~icons/lucide/rss')['default']
|
||||
IconLucideSearch: typeof import('~icons/lucide/search')['default']
|
||||
IconLucideTriangleAlert: typeof import('~icons/lucide/triangle-alert')['default']
|
||||
IconLucideUsers: typeof import('~icons/lucide/users')['default']
|
||||
|
|
@ -254,6 +255,7 @@ declare module 'vue' {
|
|||
LensesRenderersVideoLensRenderer: typeof import('./components/lenses/renderers/VideoLensRenderer.vue')['default']
|
||||
LensesRenderersXMLLensRenderer: typeof import('./components/lenses/renderers/XMLLensRenderer.vue')['default']
|
||||
LensesResponseBodyRenderer: typeof import('./components/lenses/ResponseBodyRenderer.vue')['default']
|
||||
MonacoScriptEditor: typeof import('./components/MonacoScriptEditor.vue')['default']
|
||||
ProfileUserDelete: typeof import('./components/profile/UserDelete.vue')['default']
|
||||
RealtimeCommunication: typeof import('./components/realtime/Communication.vue')['default']
|
||||
RealtimeConnectionConfig: typeof import('./components/realtime/ConnectionConfig.vue')['default']
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
:model-value="name"
|
||||
:placeholder="t('count.key')"
|
||||
:auto-complete-source="keyAutoCompleteSource"
|
||||
:auto-complete-env="true"
|
||||
:auto-complete-env="autoCompleteEnv"
|
||||
:envs="envs"
|
||||
:inspection-results="inspectionKeyResult"
|
||||
@update:model-value="emit('update:name', $event)"
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
:class="{ 'opacity-50': !entityActive }"
|
||||
:model-value="value"
|
||||
:placeholder="t('count.value')"
|
||||
:auto-complete-env="true"
|
||||
:auto-complete-env="autoCompleteEnv"
|
||||
:envs="envs"
|
||||
:inspection-results="inspectionValueResult"
|
||||
@update:model-value="emit('update:value', $event)"
|
||||
|
|
@ -56,7 +56,10 @@
|
|||
"
|
||||
/>
|
||||
|
||||
<slot name="after-value"></slot>
|
||||
|
||||
<input
|
||||
v-if="showDescription"
|
||||
:value="description"
|
||||
:placeholder="t('count.description')"
|
||||
class="flex flex-1 px-4 bg-transparent"
|
||||
|
|
@ -133,7 +136,9 @@ type Entity = {
|
|||
|
||||
const t = useI18n()
|
||||
|
||||
defineProps<{
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
showDescription?: boolean
|
||||
total: number
|
||||
index: number
|
||||
entityId: number
|
||||
|
|
@ -145,8 +150,19 @@ defineProps<{
|
|||
inspectionValueResult?: InspectorResult[]
|
||||
description?: string
|
||||
envs?: AggregateEnvironment[]
|
||||
autoCompleteEnv?: boolean
|
||||
keyAutoCompleteSource?: string[]
|
||||
}>()
|
||||
}>(),
|
||||
{
|
||||
showDescription: true,
|
||||
description: "",
|
||||
inspectionKeyResult: () => [],
|
||||
inspectionValueResult: () => [],
|
||||
envs: () => [],
|
||||
autoCompleteEnv: true,
|
||||
keyAutoCompleteSource: () => [],
|
||||
}
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "update:name", value: string): void
|
||||
|
|
|
|||
|
|
@ -172,6 +172,364 @@
|
|||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<div
|
||||
class="flex cursor-pointer items-center justify-between py-2 pl-4 text-secondaryLight transition hover:text-secondary"
|
||||
@click="toggleAdvancedConfig"
|
||||
>
|
||||
<span class="select-none">{{ t("authorization.advance_config") }}</span>
|
||||
<IconChevronDown
|
||||
v-if="!isAdvancedConfigExpanded"
|
||||
class="mr-4 opacity-50"
|
||||
/>
|
||||
<IconChevronUp v-else class="mr-4 opacity-50" />
|
||||
</div>
|
||||
|
||||
<div v-show="isAdvancedConfigExpanded">
|
||||
<div class="flex flex-col border-t border-dividerLight">
|
||||
<!-- Auth Request Parameters Section -->
|
||||
<div class="border-b border-dividerLight">
|
||||
<div class="flex items-center justify-between p-4">
|
||||
<label class="font-semibold text-secondaryLight">
|
||||
{{ t("authorization.oauth.auth_request") }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<!-- Column Headers -->
|
||||
<div
|
||||
class="flex border-b divide-x divide-dividerLight border-dividerLight bg-primaryLight"
|
||||
>
|
||||
<span class="w-8"></span>
|
||||
<!-- Drag handle space -->
|
||||
<span
|
||||
class="flex-1 px-4 py-2 text-xs font-semibold text-secondaryLight"
|
||||
>
|
||||
{{ t("count.key") }}
|
||||
</span>
|
||||
<span
|
||||
class="flex-1 px-4 py-2 text-xs font-semibold text-secondaryLight"
|
||||
>
|
||||
{{ t("count.value") }}
|
||||
</span>
|
||||
<span class="w-8"></span>
|
||||
<!-- Active/Inactive toggle space -->
|
||||
<span class="w-8"></span>
|
||||
<!-- Delete button space -->
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="!workingAuthRequestParams.length"
|
||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
||||
>
|
||||
<span class="text-center">
|
||||
{{ t("empty.parameters") }}
|
||||
</span>
|
||||
|
||||
<HoppButtonSecondary
|
||||
class="mt-2"
|
||||
:icon="IconPlus"
|
||||
:label="`${t('action.add')}`"
|
||||
@click="addAuthRequestParam()"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Parameter rows -->
|
||||
<div class="divide-y divide-dividerLight" v-else>
|
||||
<HttpKeyValue
|
||||
:show-description="false"
|
||||
v-for="(param, index) in workingAuthRequestParams"
|
||||
:key="`auth-request-param-${param.id}`"
|
||||
v-model:name="param.key"
|
||||
v-model:value="param.value"
|
||||
:total="workingAuthRequestParams.length"
|
||||
:index="index"
|
||||
:entity-id="param.id"
|
||||
:entity-active="param.active"
|
||||
:is-active="param.hasOwnProperty('active')"
|
||||
:envs="envs"
|
||||
:auto-complete-env="true"
|
||||
:key-auto-complete-source="commonOAuth2AuthParams"
|
||||
@update-entity="
|
||||
updateAuthRequestParam($event.index, $event.payload)
|
||||
"
|
||||
@delete-entity="deleteAuthRequestParam($event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col border-t border-dividerLight">
|
||||
<!-- Token Request Parameters Section -->
|
||||
<div class="border-b border-dividerLight">
|
||||
<div class="flex items-center justify-between p-4">
|
||||
<label class="font-semibold text-secondaryLight">
|
||||
{{ t("authorization.oauth.token_request") }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<!-- Column Headers -->
|
||||
<div
|
||||
class="flex border-b divide-x divide-dividerLight border-dividerLight bg-primaryLight"
|
||||
>
|
||||
<span class="w-8"></span>
|
||||
<!-- Drag handle space -->
|
||||
<span
|
||||
class="flex-1 px-4 py-2 text-xs font-semibold text-secondaryLight"
|
||||
>
|
||||
{{ t("count.key") }}
|
||||
</span>
|
||||
<span
|
||||
class="flex-1 px-4 py-2 text-xs font-semibold text-secondaryLight"
|
||||
>
|
||||
{{ t("count.value") }}
|
||||
</span>
|
||||
<span
|
||||
class="flex-1 px-4 py-2 text-xs font-semibold text-secondaryLight"
|
||||
>
|
||||
{{ t("authorization.oauth.send_in") }}
|
||||
</span>
|
||||
<span class="w-8"></span>
|
||||
<!-- Active/Inactive toggle space -->
|
||||
<span class="w-8"></span>
|
||||
<!-- Delete button space -->
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="!workingTokenRequestParams.length"
|
||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
||||
>
|
||||
<span class="text-center">
|
||||
{{ t("empty.parameters") }}
|
||||
</span>
|
||||
|
||||
<HoppButtonSecondary
|
||||
class="mt-2"
|
||||
:icon="IconPlus"
|
||||
:label="`${t('action.add')}`"
|
||||
@click="addTokenRequestParam()"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Parameter rows -->
|
||||
<div class="divide-y divide-dividerLight" v-else>
|
||||
<HttpKeyValue
|
||||
:show-description="false"
|
||||
v-for="(param, index) in workingTokenRequestParams"
|
||||
:key="`token-request-param-${param.id}`"
|
||||
v-model:name="param.key"
|
||||
v-model:value="param.value"
|
||||
:total="workingTokenRequestParams.length"
|
||||
:index="index"
|
||||
:entity-id="param.id"
|
||||
:entity-active="param.active"
|
||||
:is-active="param.hasOwnProperty('active')"
|
||||
:envs="envs"
|
||||
:auto-complete-env="true"
|
||||
:key-auto-complete-source="commonOAuth2TokenParams"
|
||||
@update-entity="
|
||||
updateTokenRequestParam($event.index, {
|
||||
id: $event.payload.id,
|
||||
key: $event.payload.key,
|
||||
value: $event.payload.value,
|
||||
sendIn: param.sendIn,
|
||||
active: $event.payload.active,
|
||||
})
|
||||
"
|
||||
@delete-entity="deleteTokenRequestParam($event)"
|
||||
>
|
||||
<template #after-value>
|
||||
<div class="flex flex-1">
|
||||
<tippy interactive trigger="click" theme="popover">
|
||||
<HoppSmartSelectWrapper>
|
||||
<HoppButtonSecondary
|
||||
:class="{ 'opacity-50': !param.active }"
|
||||
class="flex-1 rounded-none text-left"
|
||||
:label="
|
||||
sendInOptions.find(
|
||||
(option) => option.value === param.sendIn
|
||||
)?.label || t('authorization.oauth.send_in')
|
||||
"
|
||||
/>
|
||||
</HoppSmartSelectWrapper>
|
||||
<template #content="{ hide }">
|
||||
<div
|
||||
class="flex flex-col focus:outline-none"
|
||||
tabindex="0"
|
||||
@keyup.escape="hide()"
|
||||
>
|
||||
<HoppSmartItem
|
||||
v-for="option in sendInOptions"
|
||||
:key="option.value"
|
||||
:label="option.label"
|
||||
:icon="
|
||||
param.sendIn === option.value
|
||||
? IconCircleDot
|
||||
: IconCircle
|
||||
"
|
||||
:active="param.sendIn === option.value"
|
||||
@click="
|
||||
() => {
|
||||
updateTokenRequestParam(index, {
|
||||
...param,
|
||||
sendIn: option.value as
|
||||
| 'headers'
|
||||
| 'body'
|
||||
| 'url',
|
||||
})
|
||||
hide()
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</tippy>
|
||||
</div>
|
||||
</template>
|
||||
</HttpKeyValue>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col border-t border-dividerLight">
|
||||
<!-- Refresh Request Parameters Section -->
|
||||
<div class="border-b border-dividerLight">
|
||||
<div class="flex items-center justify-between p-4">
|
||||
<label class="font-semibold text-secondaryLight">
|
||||
{{ t("authorization.oauth.refresh_request") }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<!-- Column Headers -->
|
||||
<div
|
||||
class="flex border-b divide-x divide-dividerLight border-dividerLight bg-primaryLight"
|
||||
>
|
||||
<span class="w-8"></span>
|
||||
<!-- Drag handle space -->
|
||||
<span
|
||||
class="flex-1 px-4 py-2 text-xs font-semibold text-secondaryLight"
|
||||
>
|
||||
{{ t("count.key") }}
|
||||
</span>
|
||||
<span
|
||||
class="flex-1 px-4 py-2 text-xs font-semibold text-secondaryLight"
|
||||
>
|
||||
{{ t("count.value") }}
|
||||
</span>
|
||||
<span
|
||||
class="flex-1 px-4 py-2 text-xs font-semibold text-secondaryLight"
|
||||
>
|
||||
{{ t("authorization.oauth.send_in") }}
|
||||
</span>
|
||||
<span class="w-8"></span>
|
||||
<!-- Active/Inactive toggle space -->
|
||||
<span class="w-8"></span>
|
||||
<!-- Delete button space -->
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="!workingRefreshRequestParams.length"
|
||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
||||
>
|
||||
<span class="text-center">
|
||||
{{ t("empty.parameters") }}
|
||||
</span>
|
||||
|
||||
<HoppButtonSecondary
|
||||
class="mt-2"
|
||||
:icon="IconPlus"
|
||||
:label="`${t('action.add')}`"
|
||||
@click="addRefreshRequestParam()"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Parameter rows -->
|
||||
<div class="divide-y divide-dividerLight" v-else>
|
||||
<HttpKeyValue
|
||||
:show-description="false"
|
||||
v-for="(param, index) in workingRefreshRequestParams"
|
||||
:key="`refresh-request-param-${param.id}`"
|
||||
v-model:name="param.key"
|
||||
v-model:value="param.value"
|
||||
:total="workingRefreshRequestParams.length"
|
||||
:index="index"
|
||||
:entity-id="param.id"
|
||||
:entity-active="param.active"
|
||||
:is-active="param.hasOwnProperty('active')"
|
||||
:envs="envs"
|
||||
:auto-complete-env="true"
|
||||
:key-auto-complete-source="commonOAuth2RefreshParams"
|
||||
@update-entity="
|
||||
updateRefreshRequestParam($event.index, {
|
||||
id: $event.payload.id,
|
||||
key: $event.payload.key,
|
||||
value: $event.payload.value,
|
||||
sendIn: param.sendIn,
|
||||
active: $event.payload.active,
|
||||
})
|
||||
"
|
||||
@delete-entity="deleteRefreshRequestParam($event)"
|
||||
>
|
||||
<template #after-value>
|
||||
<div class="flex flex-1">
|
||||
<tippy interactive trigger="click" theme="popover">
|
||||
<HoppSmartSelectWrapper>
|
||||
<HoppButtonSecondary
|
||||
:class="{ 'opacity-50': !param.active }"
|
||||
class="flex-1 rounded-none text-left"
|
||||
:label="
|
||||
sendInOptions.find(
|
||||
(option) => option.value === param.sendIn
|
||||
)?.label || t('authorization.oauth.send_in')
|
||||
"
|
||||
/>
|
||||
</HoppSmartSelectWrapper>
|
||||
<template #content="{ hide }">
|
||||
<div
|
||||
class="flex flex-col focus:outline-none"
|
||||
tabindex="0"
|
||||
@keyup.escape="hide()"
|
||||
>
|
||||
<HoppSmartItem
|
||||
v-for="option in sendInOptions"
|
||||
:key="option.value"
|
||||
:label="option.label"
|
||||
:icon="
|
||||
param.sendIn === option.value
|
||||
? IconCircleDot
|
||||
: IconCircle
|
||||
"
|
||||
:active="param.sendIn === option.value"
|
||||
@click="
|
||||
() => {
|
||||
updateRefreshRequestParam(index, {
|
||||
...param,
|
||||
sendIn: option.value as
|
||||
| 'headers'
|
||||
| 'body'
|
||||
| 'url',
|
||||
})
|
||||
hide()
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</tippy>
|
||||
</div>
|
||||
</template>
|
||||
</HttpKeyValue>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-2 gap-1 flex">
|
||||
<HoppButtonSecondary
|
||||
filled
|
||||
|
|
@ -192,12 +550,21 @@
|
|||
import { HoppGQLAuthOAuth2, HoppRESTAuthOAuth2 } from "@hoppscotch/data"
|
||||
import { useService } from "dioc/vue"
|
||||
import * as E from "fp-ts/Either"
|
||||
import { Ref, computed, ref } from "vue"
|
||||
import { Ref, computed, ref, watch, onMounted } from "vue"
|
||||
import { z } from "zod"
|
||||
import { useI18n } from "~/composables/i18n"
|
||||
import { refWithCallbackOnChange } from "~/composables/ref"
|
||||
import { useToast } from "~/composables/toast"
|
||||
import { replaceTemplateStringsInObjectValues } from "~/helpers/auth"
|
||||
import {
|
||||
replaceTemplateString,
|
||||
replaceTemplateStringsInObjectValues,
|
||||
} from "~/helpers/auth"
|
||||
import {
|
||||
commonOAuth2AuthParams,
|
||||
commonOAuth2RefreshParams,
|
||||
commonOAuth2TokenParams,
|
||||
sendInOptions,
|
||||
} from "~/helpers/oauth2Params"
|
||||
import { AggregateEnvironment } from "~/newstore/environments"
|
||||
import authCode, {
|
||||
AuthCodeOauthFlowParams,
|
||||
|
|
@ -225,6 +592,9 @@ import { GQLTabService } from "~/services/tab/graphql"
|
|||
import { RESTTabService } from "~/services/tab/rest"
|
||||
import IconCircle from "~icons/lucide/circle"
|
||||
import IconCircleDot from "~icons/lucide/circle-dot"
|
||||
import IconChevronDown from "~icons/lucide/chevron-down"
|
||||
import IconChevronUp from "~icons/lucide/chevron-up"
|
||||
import IconPlus from "~icons/lucide/plus"
|
||||
|
||||
const t = useI18n()
|
||||
const toast = useToast()
|
||||
|
|
@ -417,6 +787,33 @@ const supportedGrantTypes = [
|
|||
scopes: scopes.value,
|
||||
isPKCE: isPKCE.value,
|
||||
codeVerifierMethod: codeChallenge.value?.id,
|
||||
authRequestParams: workingAuthRequestParams.value
|
||||
.filter((p) => p.active && p.key && p.value)
|
||||
.map((p) => ({
|
||||
id: p.id,
|
||||
key: replaceTemplateString(p.key),
|
||||
value: replaceTemplateString(p.value),
|
||||
active: p.active,
|
||||
sendIn: p.sendIn,
|
||||
})),
|
||||
tokenRequestParams: workingTokenRequestParams.value
|
||||
.filter((p) => p.active && p.key && p.value)
|
||||
.map((p) => ({
|
||||
id: p.id,
|
||||
key: replaceTemplateString(p.key),
|
||||
value: replaceTemplateString(p.value),
|
||||
active: p.active,
|
||||
sendIn: p.sendIn,
|
||||
})),
|
||||
refreshRequestParams: workingRefreshRequestParams.value
|
||||
.filter((p) => p.active && p.key && p.value)
|
||||
.map((p) => ({
|
||||
id: p.id,
|
||||
key: replaceTemplateString(p.key),
|
||||
value: replaceTemplateString(p.value),
|
||||
active: p.active,
|
||||
sendIn: p.sendIn,
|
||||
})),
|
||||
}
|
||||
|
||||
const unwrappedParams = replaceTemplateStringsInObjectValues(params)
|
||||
|
|
@ -998,7 +1395,7 @@ const setAccessTokenInActiveContext = (
|
|||
tabService.currentActiveTab.value.document.request.auth.authType ===
|
||||
"oauth-2"
|
||||
) {
|
||||
// @ts-expect-error - todo: narrow the grantType to only supporting refresh tokens
|
||||
// @ts-expect-error - TODO: narrow the grantType to only supporting refresh tokens
|
||||
tabService.currentActiveTab.value.document.request.auth.grantTypeInfo.refreshToken =
|
||||
refreshToken
|
||||
}
|
||||
|
|
@ -1133,4 +1530,305 @@ const grantTypeTippyActions = ref<HTMLElement | null>(null)
|
|||
const pkceTippyActions = ref<HTMLElement | null>(null)
|
||||
const authTippyActions = ref<HTMLElement | null>(null)
|
||||
const clientAuthenticationTippyActions = ref<HTMLElement | null>(null)
|
||||
|
||||
// Advanced Configuration state
|
||||
const isAdvancedConfigExpanded = ref(false)
|
||||
|
||||
const toggleAdvancedConfig = () => {
|
||||
isAdvancedConfigExpanded.value = !isAdvancedConfigExpanded.value
|
||||
}
|
||||
|
||||
// Advanced Configuration: Auth Request Parameters
|
||||
type OAuth2AdvancedParam = {
|
||||
id: number
|
||||
key: string
|
||||
value: string
|
||||
active: boolean
|
||||
sendIn?: "headers" | "url" | "body"
|
||||
}
|
||||
|
||||
let paramsIdCounter = 1000
|
||||
|
||||
// Initialize working auth request params
|
||||
const workingAuthRequestParams = ref<OAuth2AdvancedParam[]>([
|
||||
{ id: paramsIdCounter++, key: "", value: "", active: true },
|
||||
])
|
||||
|
||||
// Watch for changes in working auth request params
|
||||
watch(
|
||||
workingAuthRequestParams,
|
||||
(newParams: OAuth2AdvancedParam[]) => {
|
||||
// Auto-add empty row when the last row is filled
|
||||
if (newParams.length > 0 && newParams[newParams.length - 1].key !== "") {
|
||||
workingAuthRequestParams.value.push({
|
||||
id: paramsIdCounter++,
|
||||
key: "",
|
||||
value: "",
|
||||
active: true,
|
||||
})
|
||||
}
|
||||
|
||||
// Update auth.value.grantTypeInfo with non-empty params
|
||||
const nonEmptyParams = newParams.filter(
|
||||
(p: OAuth2AdvancedParam) => p.key !== "" || p.value !== ""
|
||||
)
|
||||
|
||||
if ("authRequestParams" in auth.value.grantTypeInfo) {
|
||||
auth.value.grantTypeInfo.authRequestParams = nonEmptyParams.map(
|
||||
(param) => ({
|
||||
id: param.id,
|
||||
key: param.key,
|
||||
value: param.value,
|
||||
active: param.active,
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// Functions for auth request params management
|
||||
const addAuthRequestParam = () => {
|
||||
workingAuthRequestParams.value.push({
|
||||
id: paramsIdCounter++,
|
||||
key: "",
|
||||
value: "",
|
||||
active: true,
|
||||
})
|
||||
}
|
||||
|
||||
const updateAuthRequestParam = (
|
||||
index: number,
|
||||
payload: OAuth2AdvancedParam
|
||||
) => {
|
||||
workingAuthRequestParams.value[index] = payload
|
||||
}
|
||||
|
||||
const deleteAuthRequestParam = (index: number) => {
|
||||
// Only delete if it's not the last empty row, or if there are multiple rows
|
||||
if (workingAuthRequestParams.value.length > 1) {
|
||||
workingAuthRequestParams.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// Token Request Parameters
|
||||
interface OAuth2TokenParam {
|
||||
id: number
|
||||
key: string
|
||||
value: string
|
||||
sendIn: "headers" | "body" | "url"
|
||||
active: boolean
|
||||
}
|
||||
|
||||
// Initialize working token request params
|
||||
const workingTokenRequestParams = ref<OAuth2TokenParam[]>([
|
||||
{
|
||||
id: paramsIdCounter++,
|
||||
key: "",
|
||||
value: "",
|
||||
sendIn: "body",
|
||||
active: true,
|
||||
},
|
||||
])
|
||||
|
||||
// Watch for changes in working token request params
|
||||
watch(
|
||||
workingTokenRequestParams,
|
||||
(newParams: OAuth2TokenParam[]) => {
|
||||
// Auto-add empty row when the last row is filled
|
||||
if (newParams.length > 0 && newParams[newParams.length - 1].key !== "") {
|
||||
workingTokenRequestParams.value.push({
|
||||
id: paramsIdCounter++,
|
||||
key: "",
|
||||
value: "",
|
||||
sendIn: "body",
|
||||
active: true,
|
||||
})
|
||||
}
|
||||
|
||||
// Update auth.value.grantTypeInfo with non-empty params
|
||||
const nonEmptyParams = newParams.filter(
|
||||
(p: OAuth2TokenParam) => p.key !== "" || p.value !== ""
|
||||
)
|
||||
|
||||
if ("tokenRequestParams" in auth.value.grantTypeInfo) {
|
||||
auth.value.grantTypeInfo.tokenRequestParams = nonEmptyParams.map(
|
||||
(param) => ({
|
||||
id: param.id,
|
||||
key: param.key,
|
||||
value: param.value,
|
||||
sendIn: param.sendIn,
|
||||
active: param.active,
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// Functions for token request params management
|
||||
const addTokenRequestParam = () => {
|
||||
workingTokenRequestParams.value.push({
|
||||
id: paramsIdCounter++,
|
||||
key: "",
|
||||
value: "",
|
||||
sendIn: "body",
|
||||
active: true,
|
||||
})
|
||||
}
|
||||
|
||||
const updateTokenRequestParam = (index: number, payload: OAuth2TokenParam) => {
|
||||
workingTokenRequestParams.value[index] = payload
|
||||
}
|
||||
|
||||
const deleteTokenRequestParam = (index: number) => {
|
||||
// Only delete if it's not the last empty row, or if there are multiple rows
|
||||
if (workingTokenRequestParams.value.length > 1) {
|
||||
workingTokenRequestParams.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh Request Parameters
|
||||
interface OAuth2RefreshParam {
|
||||
id: number
|
||||
key: string
|
||||
value: string
|
||||
sendIn: "headers" | "body" | "url"
|
||||
active: boolean
|
||||
}
|
||||
|
||||
// Initialize working refresh request params
|
||||
const workingRefreshRequestParams = ref<OAuth2RefreshParam[]>([
|
||||
{
|
||||
id: paramsIdCounter++,
|
||||
key: "",
|
||||
value: "",
|
||||
sendIn: "body",
|
||||
active: true,
|
||||
},
|
||||
])
|
||||
|
||||
// Watch for changes in working refresh request params
|
||||
watch(
|
||||
workingRefreshRequestParams,
|
||||
(newParams: OAuth2RefreshParam[]) => {
|
||||
// Auto-add empty row when the last row is filled
|
||||
if (newParams.length > 0 && newParams[newParams.length - 1].key !== "") {
|
||||
workingRefreshRequestParams.value.push({
|
||||
id: paramsIdCounter++,
|
||||
key: "",
|
||||
value: "",
|
||||
sendIn: "body",
|
||||
active: true,
|
||||
})
|
||||
}
|
||||
|
||||
// Update auth.value.grantTypeInfo with non-empty params
|
||||
const nonEmptyParams = newParams.filter(
|
||||
(p: OAuth2RefreshParam) => p.key !== "" || p.value !== ""
|
||||
)
|
||||
|
||||
if ("refreshRequestParams" in auth.value.grantTypeInfo) {
|
||||
auth.value.grantTypeInfo.refreshRequestParams = nonEmptyParams.map(
|
||||
(param) => ({
|
||||
id: param.id,
|
||||
key: param.key,
|
||||
value: param.value,
|
||||
sendIn: param.sendIn,
|
||||
active: param.active,
|
||||
})
|
||||
)
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// Functions for refresh request params management
|
||||
const addRefreshRequestParam = () => {
|
||||
workingRefreshRequestParams.value.push({
|
||||
id: paramsIdCounter++,
|
||||
key: "",
|
||||
value: "",
|
||||
sendIn: "body",
|
||||
active: true,
|
||||
})
|
||||
}
|
||||
|
||||
const updateRefreshRequestParam = (
|
||||
index: number,
|
||||
payload: OAuth2RefreshParam
|
||||
) => {
|
||||
workingRefreshRequestParams.value[index] = payload
|
||||
}
|
||||
|
||||
const deleteRefreshRequestParam = (index: number) => {
|
||||
// Only delete if it's not the last empty row, or if there are multiple rows
|
||||
if (workingRefreshRequestParams.value.length > 1) {
|
||||
workingRefreshRequestParams.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize advanced parameters from the auth object when component mounts
|
||||
onMounted(() => {
|
||||
if (
|
||||
"authRequestParams" in auth.value.grantTypeInfo &&
|
||||
auth.value.grantTypeInfo.authRequestParams &&
|
||||
auth.value.grantTypeInfo.authRequestParams.length > 0
|
||||
) {
|
||||
workingAuthRequestParams.value =
|
||||
auth.value.grantTypeInfo.authRequestParams.map((param) => ({
|
||||
id: param.id || paramsIdCounter++,
|
||||
key: param.key,
|
||||
value: param.value,
|
||||
active: param.active,
|
||||
}))
|
||||
}
|
||||
|
||||
if (
|
||||
"tokenRequestParams" in auth.value.grantTypeInfo &&
|
||||
auth.value.grantTypeInfo.tokenRequestParams &&
|
||||
auth.value.grantTypeInfo.tokenRequestParams.length > 0
|
||||
) {
|
||||
workingTokenRequestParams.value = [
|
||||
...auth.value.grantTypeInfo.tokenRequestParams.map((param) => ({
|
||||
id: param.id || paramsIdCounter++,
|
||||
key: param.key,
|
||||
value: param.value,
|
||||
sendIn: param.sendIn || "body",
|
||||
active: param.active,
|
||||
})),
|
||||
|
||||
{
|
||||
id: paramsIdCounter++,
|
||||
key: "",
|
||||
value: "",
|
||||
sendIn: "body",
|
||||
active: true,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
if (
|
||||
"refreshRequestParams" in auth.value.grantTypeInfo &&
|
||||
auth.value.grantTypeInfo.refreshRequestParams &&
|
||||
auth.value.grantTypeInfo.refreshRequestParams.length > 0
|
||||
) {
|
||||
workingRefreshRequestParams.value = [
|
||||
...auth.value.grantTypeInfo.refreshRequestParams.map((param) => ({
|
||||
id: param.id || paramsIdCounter++,
|
||||
key: param.key,
|
||||
value: param.value,
|
||||
sendIn: param.sendIn || "body",
|
||||
active: param.active,
|
||||
})),
|
||||
{
|
||||
id: paramsIdCounter++,
|
||||
key: "",
|
||||
value: "",
|
||||
sendIn: "body",
|
||||
active: true,
|
||||
},
|
||||
]
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -56,3 +56,7 @@ export const replaceTemplateStringsInObjectValues = <
|
|||
|
||||
return newObj as T
|
||||
}
|
||||
|
||||
export const replaceTemplateString = (str: string): string => {
|
||||
return replaceTemplateStringsInObjectValues({ value: str }).value
|
||||
}
|
||||
|
|
|
|||
|
|
@ -401,7 +401,7 @@ export const runGQLOperation = async (options: RunQueryOptions) => {
|
|||
.forEach(({ key, value }) => (finalHeaders[key] = value))
|
||||
|
||||
const gqlRequest: HoppGQLRequest = {
|
||||
v: 8,
|
||||
v: 9,
|
||||
name: options.name || "Untitled Request",
|
||||
url: finalUrl,
|
||||
headers: request.headers,
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ describe("GraphQL Response Transformation", () => {
|
|||
|
||||
describe("GraphQL Request Transformation", () => {
|
||||
const baseRequest: HoppGQLRequest = {
|
||||
v: 8,
|
||||
v: 9,
|
||||
name: "Test Query",
|
||||
url: "https://api.example.com/graphql",
|
||||
headers: [],
|
||||
|
|
@ -235,7 +235,7 @@ describe("REST Response Transformation", () => {
|
|||
}
|
||||
|
||||
const originalRequest: HoppRESTRequest = {
|
||||
v: "11",
|
||||
v: "15",
|
||||
endpoint: "https://api.example.com",
|
||||
name: "Test Request",
|
||||
method: "GET",
|
||||
|
|
@ -244,7 +244,7 @@ describe("REST Response Transformation", () => {
|
|||
preRequestScript: "",
|
||||
testScript: "",
|
||||
auth: { authType: "none", authActive: true },
|
||||
body: { contentmediaType: null, body: null },
|
||||
body: { contentType: null, body: null },
|
||||
requestVariables: [],
|
||||
responses: {},
|
||||
}
|
||||
|
|
@ -271,7 +271,7 @@ describe("REST Response Transformation", () => {
|
|||
}
|
||||
|
||||
const originalRequest: HoppRESTRequest = {
|
||||
v: "11",
|
||||
v: "15",
|
||||
endpoint: "https://api.example.com",
|
||||
name: "Test Request",
|
||||
method: "GET",
|
||||
|
|
@ -280,7 +280,7 @@ describe("REST Response Transformation", () => {
|
|||
preRequestScript: "",
|
||||
testScript: "",
|
||||
auth: { authType: "none", authActive: true },
|
||||
body: { contentmediaType: null, body: null },
|
||||
body: { contentType: null, body: null },
|
||||
requestVariables: [],
|
||||
responses: {},
|
||||
}
|
||||
|
|
@ -300,7 +300,7 @@ describe("REST Response Transformation", () => {
|
|||
|
||||
describe("REST Request Transformation", () => {
|
||||
const baseEffectiveRequest: EffectiveHoppRESTRequest = {
|
||||
v: "11",
|
||||
v: "15",
|
||||
name: "Test Request",
|
||||
method: "GET",
|
||||
endpoint: "https://api.example.com",
|
||||
|
|
@ -310,7 +310,7 @@ describe("REST Request Transformation", () => {
|
|||
preRequestScript: "",
|
||||
testScript: "",
|
||||
auth: { authType: "none", authActive: true },
|
||||
body: { contentmediaType: null, body: null },
|
||||
body: { contentType: null, body: null },
|
||||
requestVariables: [],
|
||||
responses: {},
|
||||
effectiveFinalHeaders: [],
|
||||
|
|
|
|||
59
packages/hoppscotch-common/src/helpers/oauth2Params.ts
Normal file
59
packages/hoppscotch-common/src/helpers/oauth2Params.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
export const commonOAuth2AuthParams = [
|
||||
"audience",
|
||||
"scope",
|
||||
"state",
|
||||
"nonce",
|
||||
"prompt",
|
||||
"max_age",
|
||||
"ui_locales",
|
||||
"id_token_hint",
|
||||
"login_hint",
|
||||
"acr_values",
|
||||
"response_mode",
|
||||
"display",
|
||||
"claims",
|
||||
"request",
|
||||
"request_uri",
|
||||
]
|
||||
|
||||
export const commonOAuth2TokenParams = [
|
||||
"grant_type",
|
||||
"code",
|
||||
"redirect_uri",
|
||||
"client_id",
|
||||
"client_secret",
|
||||
"code_verifier",
|
||||
"username",
|
||||
"password",
|
||||
"scope",
|
||||
"audience",
|
||||
"resource",
|
||||
"assertion",
|
||||
"assertion_type",
|
||||
"refresh_token",
|
||||
]
|
||||
|
||||
export const commonOAuth2RefreshParams = [
|
||||
"grant_type",
|
||||
"refresh_token",
|
||||
"client_id",
|
||||
"client_secret",
|
||||
"scope",
|
||||
"audience",
|
||||
"resource",
|
||||
]
|
||||
|
||||
export const sendInOptions = [
|
||||
{
|
||||
label: "Request Body",
|
||||
value: "body",
|
||||
},
|
||||
{
|
||||
label: "Request URL",
|
||||
value: "url",
|
||||
},
|
||||
{
|
||||
label: "Request Headers",
|
||||
value: "headers",
|
||||
},
|
||||
]
|
||||
|
|
@ -10,21 +10,47 @@ import { z } from "zod"
|
|||
import { getService } from "~/modules/dioc"
|
||||
import * as E from "fp-ts/Either"
|
||||
import { KernelInterceptorService } from "~/services/kernel-interceptor.service"
|
||||
import { AuthCodeGrantTypeParams } from "@hoppscotch/data"
|
||||
import { content } from "@hoppscotch/kernel"
|
||||
|
||||
const persistenceService = getService(PersistenceService)
|
||||
const interceptorService = getService(KernelInterceptorService)
|
||||
|
||||
const AuthCodeOauthFlowParamsSchema = AuthCodeGrantTypeParams.pick({
|
||||
authEndpoint: true,
|
||||
tokenEndpoint: true,
|
||||
clientID: true,
|
||||
clientSecret: true,
|
||||
scopes: true,
|
||||
isPKCE: true,
|
||||
codeVerifierMethod: true,
|
||||
})
|
||||
const AuthCodeOauthFlowParamsSchema = z
|
||||
.object({
|
||||
authEndpoint: z.string(),
|
||||
tokenEndpoint: z.string(),
|
||||
clientID: z.string(),
|
||||
clientSecret: z.string().optional(),
|
||||
scopes: z.string().optional(),
|
||||
isPKCE: z.boolean(),
|
||||
codeVerifierMethod: z.enum(["plain", "S256"]).optional(),
|
||||
authRequestParams: z.array(
|
||||
z.object({
|
||||
id: z.number(),
|
||||
key: z.string(),
|
||||
value: z.string(),
|
||||
active: z.boolean(),
|
||||
})
|
||||
),
|
||||
refreshRequestParams: z.array(
|
||||
z.object({
|
||||
id: z.number(),
|
||||
key: z.string(),
|
||||
value: z.string(),
|
||||
active: z.boolean(),
|
||||
sendIn: z.enum(["headers", "url", "body"]).optional(),
|
||||
})
|
||||
),
|
||||
tokenRequestParams: z.array(
|
||||
z.object({
|
||||
id: z.number(),
|
||||
key: z.string(),
|
||||
value: z.string(),
|
||||
active: z.boolean(),
|
||||
sendIn: z.enum(["headers", "url", "body"]).optional(),
|
||||
})
|
||||
),
|
||||
})
|
||||
.refine(
|
||||
(params) => {
|
||||
return (
|
||||
|
|
@ -63,6 +89,9 @@ export const getDefaultAuthCodeOauthFlowParams =
|
|||
scopes: undefined,
|
||||
isPKCE: false,
|
||||
codeVerifierMethod: "S256",
|
||||
authRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
tokenRequestParams: [],
|
||||
})
|
||||
|
||||
const initAuthCodeOauthFlow = async ({
|
||||
|
|
@ -73,6 +102,9 @@ const initAuthCodeOauthFlow = async ({
|
|||
authEndpoint,
|
||||
isPKCE,
|
||||
codeVerifierMethod,
|
||||
authRequestParams,
|
||||
refreshRequestParams,
|
||||
tokenRequestParams,
|
||||
}: AuthCodeOauthFlowParams) => {
|
||||
const state = generateRandomString()
|
||||
|
||||
|
|
@ -99,6 +131,24 @@ const initAuthCodeOauthFlow = async ({
|
|||
codeVerifierMethod?: string
|
||||
codeChallenge?: string
|
||||
scopes?: string
|
||||
authRequestParams?: Array<{
|
||||
key: string
|
||||
value: string
|
||||
active: boolean
|
||||
sendIn?: string
|
||||
}>
|
||||
refreshRequestParams?: Array<{
|
||||
key: string
|
||||
value: string
|
||||
active: boolean
|
||||
sendIn?: string
|
||||
}>
|
||||
tokenRequestParams?: Array<{
|
||||
key: string
|
||||
value: string
|
||||
active: boolean
|
||||
sendIn?: string
|
||||
}>
|
||||
} = {
|
||||
state,
|
||||
grant_type: "AUTHORIZATION_CODE",
|
||||
|
|
@ -109,6 +159,9 @@ const initAuthCodeOauthFlow = async ({
|
|||
isPKCE,
|
||||
codeVerifierMethod,
|
||||
scopes,
|
||||
authRequestParams,
|
||||
refreshRequestParams,
|
||||
tokenRequestParams,
|
||||
}
|
||||
|
||||
if (codeVerifier && codeChallenge) {
|
||||
|
|
@ -160,6 +213,14 @@ const initAuthCodeOauthFlow = async ({
|
|||
url.searchParams.set("code_challenge_method", codeVerifierMethod)
|
||||
}
|
||||
|
||||
if (authRequestParams.length > 0) {
|
||||
authRequestParams.forEach((param) => {
|
||||
if (param.active && param.key && param.value) {
|
||||
url.searchParams.set(param.key, param.value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Redirect to the authorization server
|
||||
window.location.assign(url.toString())
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ const DEFAULT_SETTINGS = getDefaultSettings()
|
|||
|
||||
export const REST_COLLECTIONS_MOCK: HoppCollection[] = [
|
||||
{
|
||||
v: 8,
|
||||
v: 9,
|
||||
name: "Echo",
|
||||
requests: [
|
||||
{
|
||||
|
|
@ -57,11 +57,11 @@ export const REST_COLLECTIONS_MOCK: HoppCollection[] = [
|
|||
|
||||
export const GQL_COLLECTIONS_MOCK: HoppCollection[] = [
|
||||
{
|
||||
v: 8,
|
||||
v: 9,
|
||||
name: "Echo",
|
||||
requests: [
|
||||
{
|
||||
v: 8,
|
||||
v: 9,
|
||||
name: "Echo test",
|
||||
url: "https://echo.hoppscotch.io/graphql",
|
||||
headers: [],
|
||||
|
|
@ -182,7 +182,7 @@ export const GQL_HISTORY_MOCK: GQLHistoryEntry[] = [
|
|||
{
|
||||
v: 1,
|
||||
request: {
|
||||
v: 8,
|
||||
v: 9,
|
||||
name: "Untitled",
|
||||
url: "https://echo.hoppscotch.io/graphql",
|
||||
query: "query Request { url }",
|
||||
|
|
@ -203,7 +203,7 @@ export const GQL_TAB_STATE_MOCK: PersistableTabState<HoppGQLDocument> = {
|
|||
tabID: "5edbe8d4-65c9-4381-9354-5f1bf05d8ccc",
|
||||
doc: {
|
||||
request: {
|
||||
v: 8,
|
||||
v: 9,
|
||||
name: "Untitled",
|
||||
url: "https://echo.hoppscotch.io/graphql",
|
||||
headers: [],
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import V5_VERSION from "./v/5"
|
|||
import V6_VERSION from "./v/6"
|
||||
import V7_VERSION from "./v/7"
|
||||
import V8_VERSION from "./v/8"
|
||||
import V9_VERSION from "./v/9"
|
||||
|
||||
import { z } from "zod"
|
||||
import { translateToNewRequest } from "../rest"
|
||||
|
|
@ -19,7 +20,7 @@ const versionedObject = z.object({
|
|||
})
|
||||
|
||||
export const HoppCollection = createVersionedEntity({
|
||||
latestVersion: 8,
|
||||
latestVersion: 9,
|
||||
versionMap: {
|
||||
1: V1_VERSION,
|
||||
2: V2_VERSION,
|
||||
|
|
@ -29,6 +30,7 @@ export const HoppCollection = createVersionedEntity({
|
|||
6: V6_VERSION,
|
||||
7: V7_VERSION,
|
||||
8: V8_VERSION,
|
||||
9: V9_VERSION,
|
||||
},
|
||||
getVersion(data) {
|
||||
const versionCheck = versionedObject.safeParse(data)
|
||||
|
|
@ -44,7 +46,7 @@ export const HoppCollection = createVersionedEntity({
|
|||
|
||||
export type HoppCollection = InferredEntity<typeof HoppCollection>
|
||||
|
||||
export const CollectionSchemaVersion = 8
|
||||
export const CollectionSchemaVersion = 9
|
||||
|
||||
/**
|
||||
* Generates a Collection object. This ignores the version number object
|
||||
|
|
|
|||
92
packages/hoppscotch-data/src/collection/v/9.ts
Normal file
92
packages/hoppscotch-data/src/collection/v/9.ts
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import { defineVersion, entityRefUptoVersion } from "verzod"
|
||||
import { z } from "zod"
|
||||
|
||||
import { HoppGQLAuth } from "../../graphql/v/9"
|
||||
import { HoppRESTAuth } from "../../rest/v/15/auth"
|
||||
|
||||
import { HoppCollection } from ".."
|
||||
import { v8_baseCollectionSchema, V8_SCHEMA } from "./8"
|
||||
|
||||
export const v9_baseCollectionSchema = v8_baseCollectionSchema.extend({
|
||||
v: z.literal(9),
|
||||
auth: z.union([HoppRESTAuth, HoppGQLAuth]),
|
||||
})
|
||||
|
||||
type Input = z.input<typeof v9_baseCollectionSchema> & {
|
||||
folders: Input[]
|
||||
}
|
||||
|
||||
type Output = z.output<typeof v9_baseCollectionSchema> & {
|
||||
folders: Output[]
|
||||
}
|
||||
|
||||
export const V9_SCHEMA = v9_baseCollectionSchema.extend({
|
||||
folders: z.lazy(() => z.array(entityRefUptoVersion(HoppCollection, 9))),
|
||||
}) as z.ZodType<Output, z.ZodTypeDef, Input>
|
||||
|
||||
export default defineVersion({
|
||||
initial: false,
|
||||
schema: V9_SCHEMA,
|
||||
up(old: z.infer<typeof V8_SCHEMA>) {
|
||||
// Migrate auth field if it's OAuth2 to include new advanced parameters
|
||||
let newAuth: z.infer<typeof V9_SCHEMA>["auth"]
|
||||
if (old.auth.authType === "oauth-2") {
|
||||
const oldGrantTypeInfo = old.auth.grantTypeInfo
|
||||
let newGrantTypeInfo
|
||||
|
||||
// Add the advanced parameters to the appropriate grant type
|
||||
if (oldGrantTypeInfo.grantType === "AUTHORIZATION_CODE") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
authRequestParams: [],
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
}
|
||||
} else if (oldGrantTypeInfo.grantType === "CLIENT_CREDENTIALS") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
}
|
||||
} else if (oldGrantTypeInfo.grantType === "PASSWORD") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
}
|
||||
} else if (oldGrantTypeInfo.grantType === "IMPLICIT") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
authRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
}
|
||||
} else {
|
||||
newGrantTypeInfo = oldGrantTypeInfo
|
||||
}
|
||||
|
||||
newAuth = {
|
||||
...old.auth,
|
||||
grantTypeInfo: newGrantTypeInfo,
|
||||
} as z.infer<typeof V9_SCHEMA>["auth"]
|
||||
} else {
|
||||
newAuth = old.auth
|
||||
}
|
||||
|
||||
const result: z.infer<typeof V9_SCHEMA> = {
|
||||
...old,
|
||||
v: 9 as const,
|
||||
auth: newAuth,
|
||||
folders: old.folders.map((folder) => {
|
||||
const result = HoppCollection.safeParseUpToVersion(folder, 9)
|
||||
|
||||
if (result.type !== "ok") {
|
||||
throw new Error("Failed to migrate child collections")
|
||||
}
|
||||
|
||||
return result.value
|
||||
}),
|
||||
}
|
||||
|
||||
return result
|
||||
},
|
||||
})
|
||||
|
|
@ -8,6 +8,7 @@ import V5_VERSION from "./v/5"
|
|||
import V6_VERSION from "./v/6"
|
||||
import V7_VERSION from "./v/7"
|
||||
import V8_VERSION from "./v/8"
|
||||
import V9_VERSION from "./v/9"
|
||||
|
||||
export {
|
||||
HoppGQLAuthBasic,
|
||||
|
|
@ -19,16 +20,16 @@ export {
|
|||
export { HoppGQLAuthAPIKey } from "./v/4"
|
||||
|
||||
export { GQLHeader, HoppGQLAuthAWSSignature } from "./v/6"
|
||||
export { HoppGQLAuth, HoppGQLAuthOAuth2 } from "./v/8"
|
||||
export { HoppGQLAuth, HoppGQLAuthOAuth2 } from "./v/9"
|
||||
|
||||
export const GQL_REQ_SCHEMA_VERSION = 8
|
||||
export const GQL_REQ_SCHEMA_VERSION = 9
|
||||
|
||||
const versionedObject = z.object({
|
||||
v: z.number(),
|
||||
})
|
||||
|
||||
export const HoppGQLRequest = createVersionedEntity({
|
||||
latestVersion: 8,
|
||||
latestVersion: 9,
|
||||
versionMap: {
|
||||
1: V1_VERSION,
|
||||
2: V2_VERSION,
|
||||
|
|
@ -38,6 +39,7 @@ export const HoppGQLRequest = createVersionedEntity({
|
|||
6: V6_VERSION,
|
||||
7: V7_VERSION,
|
||||
8: V8_VERSION,
|
||||
9: V9_VERSION,
|
||||
},
|
||||
getVersion(x) {
|
||||
const result = versionedObject.safeParse(x)
|
||||
|
|
|
|||
94
packages/hoppscotch-data/src/graphql/v/9.ts
Normal file
94
packages/hoppscotch-data/src/graphql/v/9.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import { defineVersion } from "verzod"
|
||||
import { z } from "zod"
|
||||
|
||||
import {
|
||||
HoppGQLAuthBasic,
|
||||
HoppGQLAuthBearer,
|
||||
HoppGQLAuthInherit,
|
||||
HoppGQLAuthNone,
|
||||
} from "./2"
|
||||
import { HoppGQLAuthAPIKey } from "./4"
|
||||
import { HoppGQLAuthAWSSignature } from "./6"
|
||||
import { HoppRESTAuthOAuth2 } from "../../rest/v/15/auth"
|
||||
import { V8_SCHEMA } from "./8"
|
||||
|
||||
export { HoppRESTAuthOAuth2 as HoppGQLAuthOAuth2 } from "../../rest/v/15/auth"
|
||||
|
||||
export const HoppGQLAuth = z
|
||||
.discriminatedUnion("authType", [
|
||||
HoppGQLAuthNone,
|
||||
HoppGQLAuthInherit,
|
||||
HoppGQLAuthBasic,
|
||||
HoppGQLAuthBearer,
|
||||
HoppRESTAuthOAuth2,
|
||||
HoppGQLAuthAPIKey,
|
||||
HoppGQLAuthAWSSignature,
|
||||
])
|
||||
.and(
|
||||
z.object({
|
||||
authActive: z.boolean(),
|
||||
})
|
||||
)
|
||||
|
||||
export type HoppGQLAuth = z.infer<typeof HoppGQLAuth>
|
||||
|
||||
export const V9_SCHEMA = V8_SCHEMA.extend({
|
||||
v: z.literal(9),
|
||||
auth: HoppGQLAuth,
|
||||
})
|
||||
|
||||
export default defineVersion({
|
||||
schema: V9_SCHEMA,
|
||||
initial: false,
|
||||
up(old: z.infer<typeof V8_SCHEMA>) {
|
||||
// If the auth is OAuth2, migrate it to include the new advanced parameters
|
||||
let newAuth: z.infer<typeof HoppGQLAuth>
|
||||
if (old.auth.authType === "oauth-2") {
|
||||
const oldGrantTypeInfo = old.auth.grantTypeInfo
|
||||
let newGrantTypeInfo
|
||||
|
||||
// Add the advanced parameters to the appropriate grant type
|
||||
if (oldGrantTypeInfo.grantType === "AUTHORIZATION_CODE") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
authRequestParams: [],
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
}
|
||||
} else if (oldGrantTypeInfo.grantType === "CLIENT_CREDENTIALS") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
}
|
||||
} else if (oldGrantTypeInfo.grantType === "PASSWORD") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
}
|
||||
} else if (oldGrantTypeInfo.grantType === "IMPLICIT") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
authRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
}
|
||||
} else {
|
||||
newGrantTypeInfo = oldGrantTypeInfo
|
||||
}
|
||||
|
||||
newAuth = {
|
||||
...old.auth,
|
||||
grantTypeInfo: newGrantTypeInfo,
|
||||
} as z.infer<typeof HoppGQLAuth>
|
||||
} else {
|
||||
newAuth = old.auth
|
||||
}
|
||||
|
||||
return {
|
||||
...old,
|
||||
v: 9 as const,
|
||||
auth: newAuth,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
|
@ -5,6 +5,7 @@ import V2_VERSION from "./v/2"
|
|||
import V3_VERSION from "./v/3"
|
||||
import V4_VERSION from "./v/4"
|
||||
import V5_VERSION from "./v/5"
|
||||
import V6_VERSION from "./v/6"
|
||||
|
||||
const versionedObject = z.object({
|
||||
// v is a stringified number
|
||||
|
|
@ -12,13 +13,14 @@ const versionedObject = z.object({
|
|||
})
|
||||
|
||||
export const HoppRESTResponseOriginalRequest = createVersionedEntity({
|
||||
latestVersion: 5,
|
||||
latestVersion: 6,
|
||||
versionMap: {
|
||||
1: V1_VERSION,
|
||||
2: V2_VERSION,
|
||||
3: V3_VERSION,
|
||||
4: V4_VERSION,
|
||||
5: V5_VERSION,
|
||||
6: V6_VERSION,
|
||||
},
|
||||
getVersion(data) {
|
||||
const versionCheck = versionedObject.safeParse(data)
|
||||
|
|
@ -32,7 +34,7 @@ export const HoppRESTResponseOriginalRequest = createVersionedEntity({
|
|||
},
|
||||
})
|
||||
|
||||
export const HoppRESTResOriginalReqSchemaVersion = "5"
|
||||
export const HoppRESTResOriginalReqSchemaVersion = "6"
|
||||
|
||||
export type HoppRESTResponseOriginalRequest = InferredEntity<
|
||||
typeof HoppRESTResponseOriginalRequest
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
import { defineVersion } from "verzod"
|
||||
import { z } from "zod"
|
||||
import { V5_SCHEMA } from "./5"
|
||||
import { HoppRESTAuth } from "../../../rest/v/15/auth"
|
||||
|
||||
export const V6_SCHEMA = V5_SCHEMA.extend({
|
||||
v: z.literal("6"),
|
||||
auth: HoppRESTAuth,
|
||||
})
|
||||
|
||||
export default defineVersion({
|
||||
initial: false,
|
||||
schema: V6_SCHEMA,
|
||||
up(old: z.infer<typeof V5_SCHEMA>) {
|
||||
// If the auth is OAuth2, migrate it to include the new advanced parameters
|
||||
let newAuth: z.infer<typeof HoppRESTAuth>
|
||||
if (old.auth.authType === "oauth-2") {
|
||||
const oldGrantTypeInfo = old.auth.grantTypeInfo
|
||||
let newGrantTypeInfo
|
||||
|
||||
// Add the advanced parameters to the appropriate grant type
|
||||
if (oldGrantTypeInfo.grantType === "AUTHORIZATION_CODE") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
authRequestParams: [],
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
}
|
||||
} else if (oldGrantTypeInfo.grantType === "CLIENT_CREDENTIALS") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
}
|
||||
} else if (oldGrantTypeInfo.grantType === "PASSWORD") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
}
|
||||
} else if (oldGrantTypeInfo.grantType === "IMPLICIT") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
authRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
}
|
||||
} else {
|
||||
newGrantTypeInfo = oldGrantTypeInfo
|
||||
}
|
||||
|
||||
newAuth = {
|
||||
...old.auth,
|
||||
grantTypeInfo: newGrantTypeInfo,
|
||||
} as z.infer<typeof HoppRESTAuth>
|
||||
} else {
|
||||
newAuth = old.auth
|
||||
}
|
||||
|
||||
return {
|
||||
...old,
|
||||
v: "6" as const,
|
||||
auth: newAuth,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
|
@ -21,8 +21,9 @@ import { HoppRESTReqBody } from "./v/10/body"
|
|||
import V11_VERSION from "./v/11"
|
||||
import V12_VERSION from "./v/12"
|
||||
import V13_VERSION from "./v/13"
|
||||
import { HoppRESTAuth } from "./v/13/auth"
|
||||
import { HoppRESTAuth } from "./v/15/auth"
|
||||
import V14_VERSION from "./v/14"
|
||||
import V15_VERSION from "./v/15/index"
|
||||
import { HoppRESTRequestResponses } from "../rest-request-response"
|
||||
|
||||
export * from "./content-types"
|
||||
|
|
@ -36,32 +37,30 @@ export {
|
|||
|
||||
export { HoppRESTRequestVariables } from "./v/2"
|
||||
|
||||
export { ImplicitOauthFlowParams } from "./v/3"
|
||||
|
||||
export { HoppRESTAuthAPIKey } from "./v/4"
|
||||
|
||||
export { AuthCodeGrantTypeParams } from "./v/5"
|
||||
|
||||
export {
|
||||
HoppRESTAuthAWSSignature,
|
||||
HoppRESTHeaders,
|
||||
HoppRESTParams,
|
||||
} from "./v/7"
|
||||
|
||||
export { HoppRESTAuthDigest, PasswordGrantTypeParams } from "./v/8/auth"
|
||||
export { HoppRESTAuthDigest } from "./v/8/auth"
|
||||
|
||||
export { FormDataKeyValue } from "./v/9/body"
|
||||
|
||||
export {
|
||||
HoppRESTAuthOAuth2,
|
||||
ClientCredentialsGrantTypeParams,
|
||||
} from "./v/11/auth"
|
||||
|
||||
export { HoppRESTReqBody } from "./v/10/body"
|
||||
|
||||
export { HoppRESTAuthHAWK, HoppRESTAuthAkamaiEdgeGrid } from "./v/12/auth"
|
||||
|
||||
export { HoppRESTAuth, HoppRESTAuthJWT } from "./v/13/auth"
|
||||
export { HoppRESTAuth, HoppRESTAuthJWT } from "./v/15/auth"
|
||||
export { AuthCodeGrantTypeParams } from "./v/15/auth"
|
||||
export { PasswordGrantTypeParams } from "./v/15/auth"
|
||||
export { ImplicitOauthFlowParams } from "./v/15/auth"
|
||||
export {
|
||||
HoppRESTAuthOAuth2,
|
||||
ClientCredentialsGrantTypeParams,
|
||||
} from "./v/15/auth"
|
||||
|
||||
export {
|
||||
HoppRESTRequestResponse,
|
||||
|
|
@ -74,7 +73,7 @@ const versionedObject = z.object({
|
|||
})
|
||||
|
||||
export const HoppRESTRequest = createVersionedEntity({
|
||||
latestVersion: 14,
|
||||
latestVersion: 15,
|
||||
versionMap: {
|
||||
0: V0_VERSION,
|
||||
1: V1_VERSION,
|
||||
|
|
@ -91,6 +90,7 @@ export const HoppRESTRequest = createVersionedEntity({
|
|||
12: V12_VERSION,
|
||||
13: V13_VERSION,
|
||||
14: V14_VERSION,
|
||||
15: V15_VERSION,
|
||||
},
|
||||
getVersion(data) {
|
||||
// For V1 onwards we have the v string storing the number
|
||||
|
|
@ -133,7 +133,7 @@ const HoppRESTRequestEq = Eq.struct<HoppRESTRequest>({
|
|||
responses: lodashIsEqualEq,
|
||||
})
|
||||
|
||||
export const RESTReqSchemaVersion = "14"
|
||||
export const RESTReqSchemaVersion = "15"
|
||||
|
||||
export type HoppRESTParam = HoppRESTRequest["params"][number]
|
||||
export type HoppRESTHeader = HoppRESTRequest["headers"][number]
|
||||
|
|
|
|||
90
packages/hoppscotch-data/src/rest/v/15/auth.ts
Normal file
90
packages/hoppscotch-data/src/rest/v/15/auth.ts
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
import { z } from "zod"
|
||||
import {
|
||||
HoppRESTAuthBasic,
|
||||
HoppRESTAuthBearer,
|
||||
HoppRESTAuthInherit,
|
||||
HoppRESTAuthNone,
|
||||
} from "../1"
|
||||
import { HoppRESTAuthAPIKey } from "../4"
|
||||
import { AuthCodeGrantTypeParams as AuthCodeGrantTypeParamsOld } from "../7"
|
||||
import { HoppRESTAuthAWSSignature } from "../7"
|
||||
import {
|
||||
HoppRESTAuthDigest,
|
||||
PasswordGrantTypeParams as PasswordGrantTypeParamsOld,
|
||||
} from "../8/auth"
|
||||
import { HoppRESTAuthAkamaiEdgeGrid, HoppRESTAuthHAWK } from "../12/auth"
|
||||
import { HoppRESTAuthJWT } from "../13/auth"
|
||||
import { ClientCredentialsGrantTypeParams as ClientCredentialsGrantTypeParamsOld } from "../11/auth"
|
||||
import { ImplicitOauthFlowParams as ImplicitOauthFlowParamsOld } from "../3"
|
||||
|
||||
export { HoppRESTAuthJWT } from "../13/auth"
|
||||
|
||||
// Define the OAuth2 advanced parameter structure
|
||||
const OAuth2AdvancedParam = z.object({
|
||||
id: z.number(),
|
||||
key: z.string(),
|
||||
value: z.string(),
|
||||
active: z.boolean(),
|
||||
sendIn: z.enum(["headers", "url", "body"]).catch("headers"),
|
||||
})
|
||||
|
||||
// omit sendIn from OAuth2AuthRequestParam
|
||||
const OAuth2AuthRequestParam = OAuth2AdvancedParam.omit({ sendIn: true })
|
||||
|
||||
export const AuthCodeGrantTypeParams = AuthCodeGrantTypeParamsOld.extend({
|
||||
authRequestParams: z.array(OAuth2AuthRequestParam).optional().default([]),
|
||||
tokenRequestParams: z.array(OAuth2AdvancedParam).optional().default([]),
|
||||
refreshRequestParams: z.array(OAuth2AdvancedParam).optional().default([]),
|
||||
})
|
||||
|
||||
export const ClientCredentialsGrantTypeParams =
|
||||
ClientCredentialsGrantTypeParamsOld.extend({
|
||||
tokenRequestParams: z.array(OAuth2AdvancedParam).optional().default([]),
|
||||
refreshRequestParams: z.array(OAuth2AdvancedParam).optional().default([]),
|
||||
})
|
||||
|
||||
export const PasswordGrantTypeParams = PasswordGrantTypeParamsOld.extend({
|
||||
tokenRequestParams: z.array(OAuth2AdvancedParam).optional().default([]),
|
||||
refreshRequestParams: z.array(OAuth2AdvancedParam).optional().default([]),
|
||||
})
|
||||
|
||||
export const ImplicitOauthFlowParams = ImplicitOauthFlowParamsOld.extend({
|
||||
authRequestParams: z.array(OAuth2AuthRequestParam).optional().default([]),
|
||||
refreshRequestParams: z.array(OAuth2AdvancedParam).optional().default([]),
|
||||
})
|
||||
|
||||
// Extend OAuth2 with advanced parameters
|
||||
export const HoppRESTAuthOAuth2 = z.object({
|
||||
authType: z.literal("oauth-2"),
|
||||
grantTypeInfo: z.discriminatedUnion("grantType", [
|
||||
AuthCodeGrantTypeParams,
|
||||
ClientCredentialsGrantTypeParams,
|
||||
PasswordGrantTypeParams,
|
||||
ImplicitOauthFlowParams,
|
||||
]),
|
||||
addTo: z.enum(["HEADERS", "QUERY_PARAMS"]).catch("HEADERS"),
|
||||
})
|
||||
|
||||
export type HoppRESTAuthOAuth2 = z.infer<typeof HoppRESTAuthOAuth2>
|
||||
|
||||
export const HoppRESTAuth = z
|
||||
.discriminatedUnion("authType", [
|
||||
HoppRESTAuthNone,
|
||||
HoppRESTAuthInherit,
|
||||
HoppRESTAuthBasic,
|
||||
HoppRESTAuthBearer,
|
||||
HoppRESTAuthOAuth2,
|
||||
HoppRESTAuthAPIKey,
|
||||
HoppRESTAuthAWSSignature,
|
||||
HoppRESTAuthDigest,
|
||||
HoppRESTAuthHAWK,
|
||||
HoppRESTAuthAkamaiEdgeGrid,
|
||||
HoppRESTAuthJWT,
|
||||
])
|
||||
.and(
|
||||
z.object({
|
||||
authActive: z.boolean(),
|
||||
})
|
||||
)
|
||||
|
||||
export type HoppRESTAuth = z.infer<typeof HoppRESTAuth>
|
||||
69
packages/hoppscotch-data/src/rest/v/15/index.ts
Normal file
69
packages/hoppscotch-data/src/rest/v/15/index.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import { V14_SCHEMA } from "../14"
|
||||
|
||||
import { z } from "zod"
|
||||
import { defineVersion } from "verzod"
|
||||
|
||||
import { HoppRESTAuth } from "./auth"
|
||||
|
||||
export const V15_SCHEMA = V14_SCHEMA.extend({
|
||||
v: z.literal("15"),
|
||||
auth: HoppRESTAuth,
|
||||
})
|
||||
|
||||
const V15_VERSION = defineVersion({
|
||||
schema: V15_SCHEMA,
|
||||
initial: false,
|
||||
up(old: z.infer<typeof V14_SCHEMA>) {
|
||||
// If the auth is OAuth2, migrate it to include the new advanced parameters
|
||||
let newAuth: z.infer<typeof HoppRESTAuth>
|
||||
if (old.auth.authType === "oauth-2") {
|
||||
const oldGrantTypeInfo = old.auth.grantTypeInfo
|
||||
let newGrantTypeInfo
|
||||
|
||||
// Add the advanced parameters to the appropriate grant type
|
||||
if (oldGrantTypeInfo.grantType === "AUTHORIZATION_CODE") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
authRequestParams: [],
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
}
|
||||
} else if (oldGrantTypeInfo.grantType === "CLIENT_CREDENTIALS") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
}
|
||||
} else if (oldGrantTypeInfo.grantType === "PASSWORD") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
tokenRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
}
|
||||
} else if (oldGrantTypeInfo.grantType === "IMPLICIT") {
|
||||
newGrantTypeInfo = {
|
||||
...oldGrantTypeInfo,
|
||||
authRequestParams: [],
|
||||
refreshRequestParams: [],
|
||||
}
|
||||
} else {
|
||||
newGrantTypeInfo = oldGrantTypeInfo
|
||||
}
|
||||
|
||||
newAuth = {
|
||||
...old.auth,
|
||||
grantTypeInfo: newGrantTypeInfo,
|
||||
} as z.infer<typeof HoppRESTAuth>
|
||||
} else {
|
||||
newAuth = old.auth
|
||||
}
|
||||
|
||||
return {
|
||||
...old,
|
||||
v: "15" as const,
|
||||
auth: newAuth,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
export default V15_VERSION
|
||||
|
|
@ -134,7 +134,7 @@ function exportedCollectionToHoppCollection(
|
|||
|
||||
return {
|
||||
id: restCollection.id,
|
||||
v: 8,
|
||||
v: 9,
|
||||
name: restCollection.name,
|
||||
folders: restCollection.folders.map((folder) =>
|
||||
exportedCollectionToHoppCollection(folder, collectionType)
|
||||
|
|
@ -196,7 +196,7 @@ function exportedCollectionToHoppCollection(
|
|||
|
||||
return {
|
||||
id: gqlCollection.id,
|
||||
v: 8,
|
||||
v: 9,
|
||||
name: gqlCollection.name,
|
||||
folders: gqlCollection.folders.map((folder) =>
|
||||
exportedCollectionToHoppCollection(folder, collectionType)
|
||||
|
|
@ -374,7 +374,7 @@ function setupUserCollectionCreatedSubscription() {
|
|||
name: res.right.userCollectionCreated.title,
|
||||
folders: [],
|
||||
requests: [],
|
||||
v: 8,
|
||||
v: 9,
|
||||
auth: data.auth,
|
||||
headers: addDescriptionField(data.headers),
|
||||
})
|
||||
|
|
@ -382,7 +382,7 @@ function setupUserCollectionCreatedSubscription() {
|
|||
name: res.right.userCollectionCreated.title,
|
||||
folders: [],
|
||||
requests: [],
|
||||
v: 8,
|
||||
v: 9,
|
||||
auth: data.auth,
|
||||
headers: addDescriptionField(data.headers),
|
||||
})
|
||||
|
|
@ -607,7 +607,7 @@ function setupUserCollectionDuplicatedSubscription() {
|
|||
name,
|
||||
folders,
|
||||
requests,
|
||||
v: 8,
|
||||
v: 9,
|
||||
auth,
|
||||
headers: addDescriptionField(headers),
|
||||
}
|
||||
|
|
@ -1037,7 +1037,7 @@ function transformDuplicatedCollections(
|
|||
name,
|
||||
folders,
|
||||
requests,
|
||||
v: 8,
|
||||
v: 9,
|
||||
auth,
|
||||
headers: addDescriptionField(headers),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ function exportedCollectionToHoppCollection(
|
|||
return {
|
||||
id: restCollection.id,
|
||||
_ref_id: data._ref_id ?? generateUniqueRefId("coll"),
|
||||
v: 8,
|
||||
v: 9,
|
||||
name: restCollection.name,
|
||||
folders: restCollection.folders.map((folder) =>
|
||||
exportedCollectionToHoppCollection(folder, collectionType)
|
||||
|
|
@ -204,7 +204,7 @@ function exportedCollectionToHoppCollection(
|
|||
return {
|
||||
id: gqlCollection.id,
|
||||
_ref_id: data._ref_id ?? generateUniqueRefId("coll"),
|
||||
v: 8,
|
||||
v: 9,
|
||||
name: gqlCollection.name,
|
||||
folders: gqlCollection.folders.map((folder) =>
|
||||
exportedCollectionToHoppCollection(folder, collectionType)
|
||||
|
|
@ -383,7 +383,7 @@ function setupUserCollectionCreatedSubscription() {
|
|||
name: res.right.userCollectionCreated.title,
|
||||
folders: [],
|
||||
requests: [],
|
||||
v: 8,
|
||||
v: 9,
|
||||
_ref_id: data._ref_id,
|
||||
auth: data.auth,
|
||||
headers: addDescriptionField(data.headers),
|
||||
|
|
@ -392,7 +392,7 @@ function setupUserCollectionCreatedSubscription() {
|
|||
name: res.right.userCollectionCreated.title,
|
||||
folders: [],
|
||||
requests: [],
|
||||
v: 8,
|
||||
v: 9,
|
||||
_ref_id: data._ref_id,
|
||||
auth: data.auth,
|
||||
headers: addDescriptionField(data.headers),
|
||||
|
|
@ -620,7 +620,7 @@ function setupUserCollectionDuplicatedSubscription() {
|
|||
name,
|
||||
folders,
|
||||
requests,
|
||||
v: 8,
|
||||
v: 9,
|
||||
_ref_id,
|
||||
auth,
|
||||
headers: addDescriptionField(headers),
|
||||
|
|
@ -1054,7 +1054,7 @@ function transformDuplicatedCollections(
|
|||
folders,
|
||||
requests,
|
||||
_ref_id,
|
||||
v: 8,
|
||||
v: 9,
|
||||
auth,
|
||||
headers: addDescriptionField(headers),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ function exportedCollectionToHoppCollection(
|
|||
return {
|
||||
id: restCollection.id,
|
||||
_ref_id: data._ref_id ?? generateUniqueRefId("coll"),
|
||||
v: 8,
|
||||
v: 9,
|
||||
name: restCollection.name,
|
||||
folders: restCollection.folders.map((folder) =>
|
||||
exportedCollectionToHoppCollection(folder, collectionType)
|
||||
|
|
@ -204,7 +204,7 @@ function exportedCollectionToHoppCollection(
|
|||
return {
|
||||
id: gqlCollection.id,
|
||||
_ref_id: data._ref_id ?? generateUniqueRefId("coll"),
|
||||
v: 8,
|
||||
v: 9,
|
||||
name: gqlCollection.name,
|
||||
folders: gqlCollection.folders.map((folder) =>
|
||||
exportedCollectionToHoppCollection(folder, collectionType)
|
||||
|
|
@ -383,7 +383,7 @@ function setupUserCollectionCreatedSubscription() {
|
|||
name: res.right.userCollectionCreated.title,
|
||||
folders: [],
|
||||
requests: [],
|
||||
v: 8,
|
||||
v: 9,
|
||||
_ref_id: data._ref_id,
|
||||
auth: data.auth,
|
||||
headers: addDescriptionField(data.headers),
|
||||
|
|
@ -392,7 +392,7 @@ function setupUserCollectionCreatedSubscription() {
|
|||
name: res.right.userCollectionCreated.title,
|
||||
folders: [],
|
||||
requests: [],
|
||||
v: 8,
|
||||
v: 9,
|
||||
_ref_id: data._ref_id,
|
||||
auth: data.auth,
|
||||
headers: addDescriptionField(data.headers),
|
||||
|
|
@ -620,7 +620,7 @@ function setupUserCollectionDuplicatedSubscription() {
|
|||
name,
|
||||
folders,
|
||||
requests,
|
||||
v: 8,
|
||||
v: 9,
|
||||
_ref_id,
|
||||
auth,
|
||||
headers: addDescriptionField(headers),
|
||||
|
|
@ -1054,7 +1054,7 @@ function transformDuplicatedCollections(
|
|||
folders,
|
||||
requests,
|
||||
_ref_id,
|
||||
v: 8,
|
||||
v: 9,
|
||||
auth,
|
||||
headers: addDescriptionField(headers),
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue