From c6dc9358c9066fbe7dc2ab2335c471317e2466bb Mon Sep 17 00:00:00 2001 From: simeng-li Date: Thu, 5 Dec 2024 21:58:14 +0800 Subject: [PATCH] feat(console): feat add SAML application creation (#6857) feat(console): feat add SAML appliction creation feat add SAML application creation --- .../assets/docs/guides/generate-metadata.js | 26 +++++++++++++------ .../console/src/assets/docs/guides/index.tsx | 10 +++++++ .../assets/docs/guides/saml-idp/README.mdx | 1 + .../src/assets/docs/guides/saml-idp/index.ts | 15 +++++++++++ .../src/assets/docs/guides/saml-idp/logo.svg | 6 +++++ .../console/src/assets/docs/guides/types.ts | 2 ++ .../ApplicationCreation/CreateForm/index.tsx | 9 ++++++- .../console/src/components/Guide/hooks.ts | 5 ++-- 8 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 packages/console/src/assets/docs/guides/saml-idp/README.mdx create mode 100644 packages/console/src/assets/docs/guides/saml-idp/index.ts create mode 100644 packages/console/src/assets/docs/guides/saml-idp/logo.svg diff --git a/packages/console/src/assets/docs/guides/generate-metadata.js b/packages/console/src/assets/docs/guides/generate-metadata.js index adc18258106..a14194e0552 100644 --- a/packages/console/src/assets/docs/guides/generate-metadata.js +++ b/packages/console/src/assets/docs/guides/generate-metadata.js @@ -20,7 +20,9 @@ const data = await Promise.all( return; } - const logo = ['logo.webp', 'logo.svg', 'logo.png'].find((logo) => existsSync(`${directory}/${logo}`)); + const logo = ['logo.webp', 'logo.svg', 'logo.png'].find((logo) => + existsSync(`${directory}/${logo}`) + ); const darkLogo = ['logo-dark.webp', 'logo-dark.svg', 'logo-dark.png'].find((logo) => existsSync(`${directory}/${logo}`) ); @@ -50,7 +52,7 @@ const filename = 'index.tsx'; await fs.writeFile( filename, `/* eslint-disable max-lines */ -// This is a generated file, don't update manually.\n\nimport { lazy } from 'react';\n\nimport { type Guide } from './types';\n` +// This is a generated file, don't update manually.\n\nimport { safeLazy } from 'react-safe-lazy';\n\nimport { type Guide } from './types';\n` ); for (const { name, logo, darkLogo } of metadata) { @@ -64,7 +66,10 @@ for (const { name, logo, darkLogo } of metadata) { if (darkLogo && !darkLogo.endsWith('.svg')) { // eslint-disable-next-line no-await-in-loop - await fs.appendFile(filename, `import ${camelCase(name)}LogoDark from './${name}/${darkLogo}';\n`); + await fs.appendFile( + filename, + `import ${camelCase(name)}LogoDark from './${name}/${darkLogo}';\n` + ); } } @@ -73,8 +78,10 @@ await fs.appendFile(filename, 'export const guides: Readonly = Object.f const getLogo = ({ name, logo, isDark }) => { if (!logo) return 'undefined'; - if (logo.endsWith('.svg')) return `lazy(async () => import('./${name}/${logo}?react'))`; - return `({ className }: { readonly className?: string }) => ${name}`; + if (logo.endsWith('.svg')) return `safeLazy(async () => import('./${name}/${logo}?react'))`; + return `({ className }: { readonly className?: string }) => ${name}`; }; for (const { name, logo, darkLogo, order } of metadata) { @@ -87,11 +94,14 @@ for (const { name, logo, darkLogo, order } of metadata) { id: '${name}', Logo: ${getLogo({ name, logo })}, DarkLogo: ${getLogo({ name, logo: darkLogo, isDark: true })}, - Component: lazy(async () => import('./${name}/README.mdx')), + Component: safeLazy(async () => import('./${name}/README.mdx')), metadata: ${camelCase(name)}, },` ); } -await fs.appendFile(filename, `]); -/* eslint-enable max-lines */\n`); +await fs.appendFile( + filename, + `]); +/* eslint-enable max-lines */\n` +); diff --git a/packages/console/src/assets/docs/guides/index.tsx b/packages/console/src/assets/docs/guides/index.tsx index 87d4b0f427a..c49f640623a 100644 --- a/packages/console/src/assets/docs/guides/index.tsx +++ b/packages/console/src/assets/docs/guides/index.tsx @@ -1,5 +1,6 @@ /* eslint-disable max-lines */ // This is a generated file, don't update manually. + import { safeLazy } from 'react-safe-lazy'; import apiExpress from './api-express/index'; @@ -12,6 +13,7 @@ import nativeExpo from './native-expo/index'; import nativeFlutter from './native-flutter/index'; import nativeIosSwift from './native-ios-swift/index'; import protectedApp from './protected-app/index'; +import samlIdp from './saml-idp/index'; import spaAngular from './spa-angular/index'; import spaChromeExtension from './spa-chrome-extension/index'; import spaReact from './spa-react/index'; @@ -321,6 +323,14 @@ export const guides: Readonly = Object.freeze([ Component: safeLazy(async () => import('./api-spring-boot/README.mdx')), metadata: apiSpringBoot, }, + { + order: Number.POSITIVE_INFINITY, + id: 'saml-idp', + Logo: safeLazy(async () => import('./saml-idp/logo.svg?react')), + DarkLogo: undefined, + Component: safeLazy(async () => import('./saml-idp/README.mdx')), + metadata: samlIdp, + }, { order: Number.POSITIVE_INFINITY, id: 'third-party-oidc', diff --git a/packages/console/src/assets/docs/guides/saml-idp/README.mdx b/packages/console/src/assets/docs/guides/saml-idp/README.mdx new file mode 100644 index 00000000000..bd96c59d218 --- /dev/null +++ b/packages/console/src/assets/docs/guides/saml-idp/README.mdx @@ -0,0 +1 @@ +# Place holder for SAML IdP guide diff --git a/packages/console/src/assets/docs/guides/saml-idp/index.ts b/packages/console/src/assets/docs/guides/saml-idp/index.ts new file mode 100644 index 00000000000..0d4e64faf4f --- /dev/null +++ b/packages/console/src/assets/docs/guides/saml-idp/index.ts @@ -0,0 +1,15 @@ +import { ApplicationType } from '@logto/schemas'; + +import { type GuideMetadata } from '../types'; + +const metadata = Object.freeze({ + name: 'SAML', + description: 'Use Logto as a SAML identity provider (IdP) for your application.', + target: ApplicationType.SAML, + isThirdParty: true, + skipGuideAfterCreation: true, + isCloud: true, + isDevFeature: true, +} satisfies GuideMetadata); + +export default metadata; diff --git a/packages/console/src/assets/docs/guides/saml-idp/logo.svg b/packages/console/src/assets/docs/guides/saml-idp/logo.svg new file mode 100644 index 00000000000..1c1affc839c --- /dev/null +++ b/packages/console/src/assets/docs/guides/saml-idp/logo.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/packages/console/src/assets/docs/guides/types.ts b/packages/console/src/assets/docs/guides/types.ts index dcc99e60374..ca2863c4690 100644 --- a/packages/console/src/assets/docs/guides/types.ts +++ b/packages/console/src/assets/docs/guides/types.ts @@ -40,6 +40,8 @@ export type GuideMetadata = { title: string; url: URL; }>; + /** Whether the guide is a development feature, that needs to be hidden from the production environment. */ + isDevFeature?: boolean; }; /** The guide instance to build in the console. */ diff --git a/packages/console/src/components/ApplicationCreation/CreateForm/index.tsx b/packages/console/src/components/ApplicationCreation/CreateForm/index.tsx index 7f9a888d995..bb6418b122f 100644 --- a/packages/console/src/components/ApplicationCreation/CreateForm/index.tsx +++ b/packages/console/src/components/ApplicationCreation/CreateForm/index.tsx @@ -10,6 +10,7 @@ import Modal from 'react-modal'; import { useSWRConfig } from 'swr'; import { GtagConversionId, reportConversion } from '@/components/Conversion/utils'; +import { isDevFeaturesEnabled } from '@/consts/env'; import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import DynamicT from '@/ds-components/DynamicT'; import FormField from '@/ds-components/FormField'; @@ -91,7 +92,13 @@ function CreateForm({ return; } - const createdApp = await api.post('api/applications', { json: data }).json(); + const appCreationEndpoint = + // TODO: @darcy remove this after the SAML is implemented + isDevFeaturesEnabled && data.type === ApplicationType.SAML + ? 'api/saml-applications' + : 'api/applications'; + + const createdApp = await api.post(appCreationEndpoint, { json: data }).json(); // Report the conversion event after the application is created. Note that the conversion // should be set as count once since this will be reported multiple times. diff --git a/packages/console/src/components/Guide/hooks.ts b/packages/console/src/components/Guide/hooks.ts index 804426c3e60..fae7aba7b12 100644 --- a/packages/console/src/components/Guide/hooks.ts +++ b/packages/console/src/components/Guide/hooks.ts @@ -3,7 +3,7 @@ import { useCallback, useMemo } from 'react'; import { guides } from '@/assets/docs/guides'; import { type Guide } from '@/assets/docs/guides/types'; -import { isCloud as isCloudEnv } from '@/consts/env'; +import { isCloud as isCloudEnv, isDevFeaturesEnabled } from '@/consts/env'; import { thirdPartyAppCategory, type AppGuideCategory, @@ -37,7 +37,8 @@ export const useAppGuideMetadata = (): { const appGuides = useMemo( () => guides.filter( - ({ metadata: { target, isCloud } }) => target !== 'API' && (isCloudEnv || !isCloud) + ({ metadata: { target, isCloud, isDevFeature } }) => + target !== 'API' && (isCloudEnv || !isCloud) && (isDevFeaturesEnabled || !isDevFeature) ), [] );