From 3287c2c232028fa3cdd10fbd8b395162eb595242 Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Tue, 7 Mar 2023 12:38:26 +0800 Subject: [PATCH] refactor: use URL class --- .../cli/src/commands/database/seed/cloud.ts | 3 ++- packages/console/src/App.tsx | 2 +- packages/console/src/consts/tenants.ts | 5 +++-- packages/console/src/hooks/use-api.ts | 4 ++-- .../components/LinkAccountSection/index.tsx | 8 +++----- packages/core/src/env-set/index.ts | 5 +++-- .../core/src/middleware/koa-auth/utils.ts | 5 +++-- .../src/middleware/koa-spa-session-guard.ts | 4 ++-- packages/core/src/oidc/adapter.ts | 19 ++++++++++--------- .../src/tests/ui-cloud/smoke.test.ts | 11 ++++------- packages/shared/src/utils/index.ts | 1 - packages/shared/src/utils/url.ts | 4 ---- 12 files changed, 33 insertions(+), 38 deletions(-) delete mode 100644 packages/shared/src/utils/url.ts diff --git a/packages/cli/src/commands/database/seed/cloud.ts b/packages/cli/src/commands/database/seed/cloud.ts index 0a724aa27..8e3013af6 100644 --- a/packages/cli/src/commands/database/seed/cloud.ts +++ b/packages/cli/src/commands/database/seed/cloud.ts @@ -1,5 +1,6 @@ import { adminConsoleApplicationId, adminTenantId, defaultTenantId } from '@logto/schemas'; -import { appendPath, GlobalValues } from '@logto/shared'; +import { GlobalValues } from '@logto/shared'; +import { appendPath } from '@silverhand/essentials'; import type { CommonQueryMethods } from 'slonik'; import { sql } from 'slonik'; diff --git a/packages/console/src/App.tsx b/packages/console/src/App.tsx index 463b5763e..c5c600c34 100644 --- a/packages/console/src/App.tsx +++ b/packages/console/src/App.tsx @@ -53,7 +53,7 @@ const Content = () => { return ( { if (isCloud) { diff --git a/packages/console/src/hooks/use-api.ts b/packages/console/src/hooks/use-api.ts index 2be101037..5be24f6f1 100644 --- a/packages/console/src/hooks/use-api.ts +++ b/packages/console/src/hooks/use-api.ts @@ -24,7 +24,7 @@ export class RequestError extends Error { } type StaticApiProps = { - prefixUrl: string; + prefixUrl?: URL; hideErrorToast?: boolean; resourceIndicator?: string; }; @@ -113,7 +113,7 @@ export const useStaticApi = ({ const useApi = (props: Omit = {}) => { const { userEndpoint } = useContext(AppEndpointsContext); - return useStaticApi({ ...props, prefixUrl: userEndpoint?.toString() ?? '' }); + return useStaticApi({ ...props, prefixUrl: userEndpoint }); }; export default useApi; diff --git a/packages/console/src/pages/Profile/components/LinkAccountSection/index.tsx b/packages/console/src/pages/Profile/components/LinkAccountSection/index.tsx index 230325302..58577065b 100644 --- a/packages/console/src/pages/Profile/components/LinkAccountSection/index.tsx +++ b/packages/console/src/pages/Profile/components/LinkAccountSection/index.tsx @@ -2,7 +2,7 @@ import { buildIdGenerator } from '@logto/core-kit'; import type { ConnectorResponse, User } from '@logto/schemas'; import { AppearanceMode } from '@logto/schemas'; import type { Optional } from '@silverhand/essentials'; -import { conditional } from '@silverhand/essentials'; +import { appendPath, conditional } from '@silverhand/essentials'; import { useCallback, useMemo } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; @@ -43,7 +43,7 @@ const LinkAccountSection = ({ user, connectors, onUpdate }: Props) => { async (connectorId: string) => { const adminTenantEndpointUrl = new URL(adminTenantEndpoint); const state = buildIdGenerator(8)(); - const redirectUri = new URL(`/callback/${connectorId}`, adminTenantEndpointUrl).toString(); + const redirectUri = new URL(`/callback/${connectorId}`, adminTenantEndpointUrl).href; const { redirectTo } = await api .post('me/social/authorization-uri', { json: { connectorId, state, redirectUri } }) .json<{ redirectTo: string }>(); @@ -60,8 +60,6 @@ const LinkAccountSection = ({ user, connectors, onUpdate }: Props) => { return []; } - const adminTenantEndpointUrl = new URL(adminTenantEndpoint); - return connectors.map(({ id, name, logo, logoDark, target }) => { const logoSrc = theme === AppearanceMode.DarkMode && logoDark ? logoDark : logo; const relatedUserDetails = user.identities[target]?.details; @@ -113,7 +111,7 @@ const LinkAccountSection = ({ user, connectors, onUpdate }: Props) => { }); const newWindow = popupWindow( - new URL(`/springboard?${queries.toString()}`, adminTenantEndpointUrl).toString(), + appendPath(adminTenantEndpoint, `/springboard?${queries.toString()}`).href, 'Link social account with Logto', 600, 640 diff --git a/packages/core/src/env-set/index.ts b/packages/core/src/env-set/index.ts index fb65c0bdb..2a02e043b 100644 --- a/packages/core/src/env-set/index.ts +++ b/packages/core/src/env-set/index.ts @@ -1,5 +1,6 @@ -import { GlobalValues, appendPath } from '@logto/shared'; +import { GlobalValues } from '@logto/shared'; import type { Optional } from '@silverhand/essentials'; +import { appendPath } from '@silverhand/essentials'; import type { PostgreSql } from '@withtyped/postgres'; import type { QueryClient } from '@withtyped/server'; import type { DatabasePool } from 'slonik'; @@ -94,7 +95,7 @@ export class EnvSet { const oidcConfigs = await getOidcConfigs(); const endpoint = getTenantEndpoint(this.tenantId, EnvSet.values); - this.#oidc = await loadOidcValues(appendPath(endpoint, '/oidc').toString(), oidcConfigs); + this.#oidc = await loadOidcValues(appendPath(endpoint, '/oidc').href, oidcConfigs); } } diff --git a/packages/core/src/middleware/koa-auth/utils.ts b/packages/core/src/middleware/koa-auth/utils.ts index edde94359..6446a22b1 100644 --- a/packages/core/src/middleware/koa-auth/utils.ts +++ b/packages/core/src/middleware/koa-auth/utils.ts @@ -7,7 +7,8 @@ import { LogtoOidcConfigKey, LogtoConfigs, } from '@logto/schemas'; -import { convertToIdentifiers, appendPath } from '@logto/shared'; +import { convertToIdentifiers } from '@logto/shared'; +import { appendPath } from '@silverhand/essentials'; import type { JWK } from 'jose'; import { sql } from 'slonik'; @@ -49,7 +50,7 @@ export const getAdminTenantTokenValidationSet = async (): Promise<{ appendPath( isMultiTenancy ? getTenantEndpoint(adminTenantId, EnvSet.values) : adminUrlSet.endpoint, '/oidc' - ).toString(), + ).href, ], }; }; diff --git a/packages/core/src/middleware/koa-spa-session-guard.ts b/packages/core/src/middleware/koa-spa-session-guard.ts index 69e872e2c..f2d8b1be9 100644 --- a/packages/core/src/middleware/koa-spa-session-guard.ts +++ b/packages/core/src/middleware/koa-spa-session-guard.ts @@ -1,4 +1,4 @@ -import { appendPath } from '@logto/shared'; +import { appendPath } from '@silverhand/essentials'; import type { MiddlewareType } from 'koa'; import type { IRouterParamContext } from 'koa-router'; import type Provider from 'oidc-provider'; @@ -31,7 +31,7 @@ export default function koaSpaSessionGuard< try { await provider.interactionDetails(ctx.req, ctx.res); } catch { - ctx.redirect(appendPath(endpoint, sessionNotFoundPath).toString()); + ctx.redirect(appendPath(endpoint, sessionNotFoundPath).href); return; } diff --git a/packages/core/src/oidc/adapter.ts b/packages/core/src/oidc/adapter.ts index 537560306..ae1f26538 100644 --- a/packages/core/src/oidc/adapter.ts +++ b/packages/core/src/oidc/adapter.ts @@ -1,6 +1,7 @@ import type { CreateApplication } from '@logto/schemas'; import { ApplicationType, adminConsoleApplicationId, demoAppApplicationId } from '@logto/schemas'; -import { tryThat, appendPath } from '@logto/shared'; +import { tryThat } from '@logto/shared'; +import { appendPath } from '@silverhand/essentials'; import { addSeconds } from 'date-fns'; import type { AdapterFactory, AllClientMetadata } from 'oidc-provider'; import { errors } from 'oidc-provider'; @@ -23,31 +24,31 @@ const transpileMetadata = (clientId: string, data: AllClientMetadata): AllClient const { adminUrlSet, cloudUrlSet } = EnvSet.values; const urls = [ - ...adminUrlSet.deduplicated().map((url) => appendPath(url, '/console').toString()), - ...cloudUrlSet.deduplicated().map(String), + ...adminUrlSet.deduplicated().map((url) => appendPath(url, '/console')), + ...cloudUrlSet.deduplicated(), ]; return { ...data, redirect_uris: [ ...(data.redirect_uris ?? []), - ...urls.map((url) => appendPath(url, '/callback').toString()), + ...urls.map((url) => appendPath(url, '/callback').href), ], - post_logout_redirect_uris: [...(data.post_logout_redirect_uris ?? []), ...urls], + post_logout_redirect_uris: [...(data.post_logout_redirect_uris ?? []), ...urls.map(String)], }; }; const buildDemoAppClientMetadata = (envSet: EnvSet): AllClientMetadata => { - const urls = getTenantUrls(envSet.tenantId, EnvSet.values).map((url) => - appendPath(url, '/demo-app').toString() + const urlStrings = getTenantUrls(envSet.tenantId, EnvSet.values).map( + (url) => appendPath(url, '/demo-app').href ); return { ...getConstantClientMetadata(envSet, ApplicationType.SPA), client_id: demoAppApplicationId, client_name: 'Demo App', - redirect_uris: urls, - post_logout_redirect_uris: urls, + redirect_uris: urlStrings, + post_logout_redirect_uris: urlStrings, }; }; diff --git a/packages/integration-tests/src/tests/ui-cloud/smoke.test.ts b/packages/integration-tests/src/tests/ui-cloud/smoke.test.ts index e1e4626d8..de00e4faf 100644 --- a/packages/integration-tests/src/tests/ui-cloud/smoke.test.ts +++ b/packages/integration-tests/src/tests/ui-cloud/smoke.test.ts @@ -1,11 +1,8 @@ -import path from 'path'; +import { appendPath } from '@silverhand/essentials'; import { logtoCloudUrl as logtoCloudUrlString, logtoConsoleUrl } from '#src/constants.js'; import { generatePassword } from '#src/utils.js'; -const appendPathname = (pathname: string, baseUrl: URL) => - new URL(path.join(baseUrl.pathname, pathname), baseUrl); - /** * NOTE: This test suite assumes test cases will run sequentially (which is Jest default). * Parallel execution will lead to errors. @@ -22,8 +19,8 @@ describe('smoke testing for cloud', () => { await page.goto(logtoCloudUrl.href); await page.waitForNavigation({ waitUntil: 'networkidle0' }); - await expect(page).toMatchElement('#app'); - expect(page.url()).toBe(appendPathname('/register', adminTenantUrl).href); + await expect(page.waitForSelector('#app')).resolves.not.toBeNull(); + expect(page.url()).toBe(appendPath(adminTenantUrl, '/register').href); }); it('can register the first admin account', async () => { @@ -33,7 +30,7 @@ describe('smoke testing for cloud', () => { await expect(page).toClick('button[name=submit]'); await page.waitForNavigation({ waitUntil: 'networkidle0' }); - expect(page.url()).toBe(appendPathname('/register/password', adminTenantUrl).href); + expect(page.url()).toBe(appendPath(adminTenantUrl, '/register/password').href); await expect(page).toFillForm('form', { newPassword: consolePassword, diff --git a/packages/shared/src/utils/index.ts b/packages/shared/src/utils/index.ts index a380f6f66..8d0e61395 100644 --- a/packages/shared/src/utils/index.ts +++ b/packages/shared/src/utils/index.ts @@ -1,4 +1,3 @@ export * from './function.js'; export * from './object.js'; export { default as findPackage } from './find-package.js'; -export * from './url.js'; diff --git a/packages/shared/src/utils/url.ts b/packages/shared/src/utils/url.ts deleted file mode 100644 index e55547760..000000000 --- a/packages/shared/src/utils/url.ts +++ /dev/null @@ -1,4 +0,0 @@ -import path from 'path'; - -export const appendPath = (url: URL | string, ...pathnames: string[]): URL => - new URL(path.join(new URL(url).pathname, ...pathnames), url);