Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Sdk snippets in test connection phase #8082

Merged
merged 10 commits into from
Sep 4, 2024
18 changes: 15 additions & 3 deletions frontend/src/component/onboarding/ConnectSdkDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -163,6 +172,9 @@ export const ConnectSdkDialog = ({
{isLargeScreen && isGenerateApiKeyStage ? (
<GenrateApiKeyConcepts />
) : null}
{isLargeScreen && isTestConnectionStage ? (
<ConceptsDefinitionsWrapper />
) : null}
</Box>
</StyledDialog>
);
Expand Down
15 changes: 5 additions & 10 deletions frontend/src/component/onboarding/GenerateApiKey.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
18 changes: 5 additions & 13 deletions frontend/src/component/onboarding/SelectSdk.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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),
Expand Down Expand Up @@ -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 },
Expand All @@ -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 },
Expand All @@ -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;
}
Expand All @@ -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>
Expand Down Expand Up @@ -155,5 +149,3 @@ export const SelectSdk: FC<ISelectSdkProps> = ({ onSelect }) => {
</SpacedContainer>
);
};

export const SelectSdkConcepts = () => {};
7 changes: 7 additions & 0 deletions frontend/src/component/onboarding/SharedComponents.tsx
Original file line number Diff line number Diff line change
@@ -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,
}));
60 changes: 60 additions & 0 deletions frontend/src/component/onboarding/TestSdkConnection.tsx
Original file line number Diff line number Diff line change
@@ -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>
);
};
2 changes: 1 addition & 1 deletion frontend/src/component/onboarding/UnleashConcepts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
183 changes: 183 additions & 0 deletions frontend/src/component/onboarding/sdkSnippets.ts
Original file line number Diff line number Diff line change
@@ -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');`,
};
Loading
Loading