1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

feat: Sdk snippets in test connection phase (#8082)

This commit is contained in:
Mateusz Kwasniewski 2024-09-04 15:43:36 +02:00 committed by GitHub
parent 5c4d0bf99b
commit ff31013c6d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 296 additions and 27 deletions

View File

@ -8,8 +8,15 @@ import {
} from '@mui/material';
import { GenerateApiKey } from './GenerateApiKey';
import { useEffect, useState } from 'react';
import { type Sdk, SelectSdk } from './SelectSdk';
import { GenrateApiKeyConcepts, SelectSdkConcepts } from './UnleashConcepts';
import { SelectSdk } from './SelectSdk';
import {
ConceptsDefinitionsWrapper,
GenrateApiKeyConcepts,
SelectSdkConcepts,
} from './UnleashConcepts';
import { TestSdkConnection } from './TestSdkConnection';
import type { Sdk } from './sharedTypes';
interface IConnectSDKDialogProps {
open: boolean;
@ -107,7 +114,9 @@ export const ConnectSdkDialog = ({
}}
/>
) : null}
{isTestConnectionStage ? <div>Last stage</div> : null}
{isTestConnectionStage ? (
<TestSdkConnection sdk={sdk} apiKey={apiKey} />
) : null}
{stage === 'generate-api-key' ? (
<Navigation>
@ -163,6 +172,9 @@ export const ConnectSdkDialog = ({
{isLargeScreen && isGenerateApiKeyStage ? (
<GenrateApiKeyConcepts />
) : null}
{isLargeScreen && isTestConnectionStage ? (
<ConceptsDefinitionsWrapper />
) : null}
</Box>
</StyledDialog>
);

View File

@ -1,8 +1,8 @@
import { useProjectApiTokens } from '../../hooks/api/getters/useProjectApiTokens/useProjectApiTokens';
import useProjectApiTokensApi from '../../hooks/api/actions/useProjectApiTokensApi/useProjectApiTokensApi';
import { useProjectApiTokens } from 'hooks/api/getters/useProjectApiTokens/useProjectApiTokens';
import useProjectApiTokensApi from 'hooks/api/actions/useProjectApiTokensApi/useProjectApiTokensApi';
import { parseToken } from './parseToken';
import useToast from '../../hooks/useToast';
import { formatUnknownError } from '../../utils/formatUnknownError';
import useToast from 'hooks/useToast';
import { formatUnknownError } from 'utils/formatUnknownError';
import {
Box,
Button,
@ -15,6 +15,7 @@ import { SingleSelectConfigButton } from '../common/DialogFormTemplate/ConfigBut
import EnvironmentsIcon from '@mui/icons-material/CloudCircle';
import { ArcherContainer, ArcherElement } from 'react-archer';
import { useEffect } from 'react';
import { SectionHeader } from './SharedComponents';
const ChooseEnvironment = ({
environments,
@ -79,12 +80,6 @@ const TokenExplanationBox = styled(Box)(({ theme }) => ({
flexWrap: 'wrap',
}));
const SectionHeader = styled('div')(({ theme }) => ({
fontWeight: theme.typography.fontWeightBold,
marginBottom: theme.spacing(1),
fontSize: theme.typography.body1.fontSize,
}));
const SectionDescription = styled('p')(({ theme }) => ({
color: theme.palette.text.secondary,
fontSize: theme.typography.body2.fontSize,

View File

@ -16,6 +16,8 @@ import rust from 'assets/icons/sdks/Logo-rust.svg';
import svelte from 'assets/icons/sdks/Logo-svelte.svg';
import vue from 'assets/icons/sdks/Logo-vue.svg';
import { formatAssetPath } from 'utils/formatPath';
import { SectionHeader } from './SharedComponents';
import type { ClientSdkName, Sdk, ServerSdkName } from './sharedTypes';
const SpacedContainer = styled('div')(({ theme }) => ({
padding: theme.spacing(5, 8, 8, 8),
@ -24,12 +26,6 @@ const SpacedContainer = styled('div')(({ theme }) => ({
gap: theme.spacing(3),
}));
const PrimarySectionHeader = styled('div')(({ theme }) => ({
fontWeight: theme.typography.fontWeightBold,
marginBottom: theme.spacing(1),
fontSize: theme.typography.body1.fontSize,
}));
const SecondarySectionHeader = styled('div')(({ theme }) => ({
marginTop: theme.spacing(4),
marginBottom: theme.spacing(2),
@ -71,7 +67,7 @@ const StyledAvatar = styled(Avatar)(({ theme }) => ({
boxShadow: theme.shadows[2],
}));
const serverSdks = [
const serverSdks: { name: ServerSdkName; icon: string }[] = [
{ name: 'Node', icon: node },
{ name: 'Golang', icon: go },
{ name: 'Ruby', icon: ruby },
@ -82,7 +78,7 @@ const serverSdks = [
{ name: 'Python', icon: python },
];
const clientSdks = [
const clientSdks: { name: ClientSdkName; icon: string }[] = [
{ name: 'Javascript', icon: javascript },
{ name: 'React', icon: react },
{ name: 'Vue', icon: vue },
@ -92,8 +88,6 @@ const clientSdks = [
{ name: 'Flutter', icon: flutter },
];
type SdkType = 'client' | 'frontend';
export type Sdk = { name: string; type: SdkType };
interface ISelectSdkProps {
onSelect: (sdk: Sdk) => void;
}
@ -102,7 +96,7 @@ export const SelectSdk: FC<ISelectSdkProps> = ({ onSelect }) => {
<SpacedContainer>
<Typography variant='h2'>Connect an SDK to Unleash</Typography>
<Box sx={{ mt: 4 }}>
<PrimarySectionHeader>Select SDK</PrimarySectionHeader>
<SectionHeader>Select SDK</SectionHeader>
<SecondarySectionHeader>
Server side SDKs
</SecondarySectionHeader>
@ -155,5 +149,3 @@ export const SelectSdk: FC<ISelectSdkProps> = ({ onSelect }) => {
</SpacedContainer>
);
};
export const SelectSdkConcepts = () => {};

View File

@ -0,0 +1,7 @@
import { styled } from '@mui/material';
export const SectionHeader = styled('div')(({ theme }) => ({
fontWeight: theme.typography.fontWeightBold,
marginBottom: theme.spacing(1),
fontSize: theme.typography.body1.fontSize,
}));

View File

@ -0,0 +1,60 @@
import type { FC } from 'react';
import { Box, styled, Typography } from '@mui/material';
import { SectionHeader } from './SharedComponents';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import type { Sdk } from './sharedTypes';
import { codeSnippets, installCommands } from './sdkSnippets';
const SpacedContainer = styled('div')(({ theme }) => ({
padding: theme.spacing(5, 8, 8, 8),
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(3),
}));
const StyledCodeBlock = styled('pre')(({ theme }) => ({
backgroundColor: theme.palette.background.elevation1,
padding: theme.spacing(2),
borderRadius: theme.shape.borderRadius,
overflow: 'auto',
fontSize: theme.typography.body2.fontSize,
wordBreak: 'break-all',
whiteSpace: 'pre-wrap',
}));
interface ITestSdkConnectionProps {
sdk: Sdk;
apiKey: string;
}
export const TestSdkConnection: FC<ITestSdkConnectionProps> = ({
sdk,
apiKey,
}) => {
const { uiConfig } = useUiConfig();
const clientApiUrl = `${uiConfig.unleashUrl}/api/`;
const frontendApiUrl = `${uiConfig.unleashUrl}/api/frontend/`;
const apiUrl = sdk.type === 'client' ? clientApiUrl : frontendApiUrl;
const codeSnippet =
codeSnippets[sdk.name] || `No snippet found for the ${sdk.name} SDK`;
const installCommand =
installCommands[sdk.name] ||
`No install command found for the ${sdk.name} SDK`;
return (
<SpacedContainer>
<Typography variant='h2'>Connect an SDK to Unleash</Typography>
<Box sx={{ mt: 4 }}>
<SectionHeader>Setup the SDK</SectionHeader>
<p>1. Install the SDK</p>
<StyledCodeBlock>{installCommand}</StyledCodeBlock>
<p>2. Initialize the SDK</p>
<StyledCodeBlock>
{codeSnippet
.replace('<YOUR_API_TOKEN>', apiKey)
.replace('<YOUR_API_URL>', apiUrl)}
</StyledCodeBlock>
</Box>
</SpacedContainer>
);
};

View File

@ -3,7 +3,7 @@ import { ProjectIcon } from '../common/ProjectIcon/ProjectIcon';
import EnvironmentsIcon from '@mui/icons-material/CloudCircle';
import CodeIcon from '@mui/icons-material/Code';
const ConceptsDefinitionsWrapper = styled('div')(({ theme }) => ({
export const ConceptsDefinitionsWrapper = styled('div')(({ theme }) => ({
backgroundColor: theme.palette.background.sidebar,
padding: theme.spacing(12, 6, 6, 6),
flex: 0,

View File

@ -0,0 +1,183 @@
import type { SdkName } from './sharedTypes';
export const installCommands: Record<SdkName, string> = {
Node: ' npm install unleash-client',
Golang: 'go get github.com/Unleash/unleash-client-go/v3',
Ruby: 'gem install unleash',
PHP: 'composer require unleash/client',
Rust: 'cargo add unleash-client',
DotNet: `dotnet add package unleash.client
// If you do not have a json library in your project:
dotnet add package Newtonsoft.Json`,
Java: `<dependency>
<groupId>io.getunleash</groupId>
<artifactId>unleash-client-java</artifactId>
<version>Latest version here</version>
</dependency>`,
Python: 'pip install UnleashClient',
Javascript: 'npm install unleash-proxy-client',
React: 'npm install @unleash/proxy-client-react unleash-proxy-client',
Vue: 'npm install @unleash/proxy-client-vue',
Svelte: 'npm install @unleash/proxy-client-svelte',
Swift: 'https://github.com/Unleash/unleash-proxy-client-swift',
Android:
'implementation("io.getunleash:unleash-android:${unleash.sdk.version}")',
Flutter: 'flutter pub add unleash_proxy_client_flutter',
};
export const codeSnippets: Record<SdkName, string> = {
Node: `import { initialize } from 'unleash-client';
const unleash = initialize({
url: '<YOUR_API_URL>',
appName: 'unleash-onboarding-node',
customHeaders: { Authorization: '<YOUR_API_TOKEN>' },
});
`,
Golang: `import (
"github.com/Unleash/unleash-client-go/v3"
)
func init() {
unleash.Initialize(
unleash.WithListener(&unleash.DebugListener{}),
unleash.WithAppName("unleash-onboarding-golang"),
unleash.WithUrl("<YOUR_API_URL>"),
unleash.WithCustomHeaders(http.Header{"Authorization": {"<YOUR_API_TOKEN>"}}),
)
}`,
Ruby: `Unleash.configure do |config|
config.app_name = 'unleash-onboarding-ruby'
config.url = '<YOUR_API_URL>'
config.custom_http_headers = {'Authorization': '<YOUR_API_TOKEN>'}
end`,
PHP: `<?php
use Unleash\\Client\\UnleashBuilder;
$unleash = UnleashBuilder::create()
->withAppName('unleash-onboarding-php')
->withAppUrl('<YOUR_API_URL>')
->withHeader('Authorization', '<YOUR_API_TOKEN>')
->withInstanceId('unleash-onboarding-instance')
->build();`,
Rust: `let client = client::ClientBuilder::default()
.interval(500)
.into_client::<UserFeatures, reqwest::Client>(
"<YOUR_API_URL>",
"unleash-onboarding-rust",
"unleash-onboarding-instance",
"<YOUR_API_TOKEN>",
)?;
client.register().await?;`,
DotNet: `using Unleash;
var settings = new UnleashSettings()
{
AppName = "unleash-onboarding-dotnet",
UnleashApi = new Uri("<YOUR_API_URL>"),
CustomHttpHeaders = new Dictionary<string, string>()
{
{"Authorization","<YOUR_API_TOKEN>" }
}
};`,
Java: `UnleashConfig config = UnleashConfig.builder()
.appName("unleash-onboarding-java")
.instanceId("unleash-onboarding-instance")
.unleashAPI("<YOUR_API_URL>")
.apiKey("<YOUR_API_TOKEN>")
.build();
Unleash unleash = new DefaultUnleash(config);`,
Python: `from UnleashClient import UnleashClient
client = UnleashClient(
url="<YOUR_API_URL>",
app_name="unleash-onboarding-python",
custom_headers={'Authorization': '<YOUR_API_TOKEN>"'})
client.initialize_client()`,
Javascript: `import { UnleashClient } from 'unleash-proxy-client';
const unleash = new UnleashClient({
url: '<YOUR_API_URL>',
clientKey: '<YOUR_API_TOKEN>',
appName: 'unleash-onboarding-javascript',
});
// Start the background polling
unleash.start();`,
React: `import { createRoot } from 'react-dom/client';
import { FlagProvider } from '@unleash/proxy-client-react';
const config = {
url: '<YOUR_API_URL>',
clientKey: '<YOUR_API_TOKEN>',
refreshInterval: 15,
appName: 'unleash-onboarding-react',
};
const root = createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<FlagProvider config={config}>
<App />
</FlagProvider>
</React.StrictMode>
);`,
Vue: `import { createApp } from 'vue'
import { plugin as unleashPlugin } from '@unleash/proxy-client-vue'
// import the root component App from a single-file component.
import App from './App.vue'
const config = {
url: '<YOUR_API_URL>'',
clientKey: '<YOUR_API_TOKEN>',
refreshInterval: 15,
appName: 'unleash-onboarding-vue',
}
const app = createApp(App)
app.use(unleashPlugin, { config })
app.mount('#app')`,
Svelte: `<script lang="ts">
import { FlagProvider } from '@unleash/proxy-client-svelte';
const config = {
url: '<YOUR_API_URL>',
clientKey: '<YOUR_API_TOKEN>',
refreshInterval: 15,
appName: 'unleash-onboarding-svelte'
};
</script>
<FlagProvider {config}>
<App />
</FlagProvider>`,
Swift: `import SwiftUI
import UnleashProxyClientSwift
var unleash = UnleashProxyClientSwift.UnleashClient(
unleashUrl: "<YOUR_API_URL>",
clientKey: "<YOUR_API_TOKEN>",
refreshInterval: 15,
appName: "unleash-onboarding-swift",
context: [:])
unleash.start()`,
Android: `val unleash = DefaultUnleash(
androidContext = applicationContext, // likely a reference to your Android application context
unleashConfig = UnleashConfig.newBuilder(appName = "unleash-onboarding-android")
.proxyUrl("<YOUR_API_URL>")
.clientKey("<YOUR_API_TOKEN>")
.pollingStrategy.interval(3000)
.metricsStrategy.interval(3000)
.build()
)`,
Flutter: `import 'package:unleash_proxy_client_flutter/unleash_proxy_client_flutter.dart';
final unleash = UnleashClient(
url: Uri.parse('<YOUR_API_URL>'),
clientKey: '<YOUR_API_TOKEN>',
appName: 'unleash-onboarding-flutter');`,
};

View File

@ -0,0 +1,20 @@
export type SdkType = 'client' | 'frontend';
export type Sdk = { name: SdkName; type: SdkType };
export type ServerSdkName =
| 'Node'
| 'Golang'
| 'Ruby'
| 'PHP'
| 'Rust'
| 'DotNet'
| 'Java'
| 'Python';
export type ClientSdkName =
| 'Javascript'
| 'React'
| 'Vue'
| 'Svelte'
| 'Swift'
| 'Android'
| 'Flutter';
export type SdkName = ServerSdkName | ClientSdkName;