Skip to content

Commit

Permalink
Add entity utilities (#221)
Browse files Browse the repository at this point in the history
  • Loading branch information
Samuel authored Apr 5, 2024
1 parent 344b672 commit a0699dd
Show file tree
Hide file tree
Showing 24 changed files with 730 additions and 361 deletions.
17 changes: 17 additions & 0 deletions apps/devtool/src/app/_components/GreenCheckStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use client'

import { faCheckCircle } from '@fortawesome/pro-regular-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'

const GreenCheckStatus = ({ isChecked, label }: { isChecked: boolean; label: string }) => {
if (!isChecked) return null

return (
<div className="flex items-center gap-4">
<FontAwesomeIcon icon={faCheckCircle} className="text-nv-green-500" />
<div className="text-nv-white">{label}</div>
</div>
)
}

export default GreenCheckStatus
14 changes: 4 additions & 10 deletions apps/devtool/src/app/_components/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,14 @@ const NavBar = () => {
<Image src="/narval-wordmark-white.png" width="150" height="50" alt="Narval Logo" priority />
</Link>
<div className="flex gap-8 ml-10 text-nv-lg">
<Link href="/policy-engine" className={`${currentPath === '/policy-engine' ? 'underline' : ''}`}>
Policy Engine
</Link>
<Link href="/vault" className={`${currentPath === '/vault' ? 'underline' : ''}`}>
Vault
<Link href="/config" className={`${currentPath === '/config' ? 'underline' : ''}`}>
Config
</Link>
<Link href="/data-store" className={`${currentPath === '/data-store' ? 'underline' : ''}`}>
Data Store
</Link>
<Link
href="/transaction-request"
className={`${currentPath === '/transaction-request' ? 'underline' : ''}`}
>
Transaction Request
<Link href="/request-playground" className={`${currentPath === '/request-playground' ? 'underline' : ''}`}>
Request Playground
</Link>
</div>
</div>
Expand Down
6 changes: 3 additions & 3 deletions apps/devtool/src/app/_hooks/useStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const useStore = () => {
const [engineClientSecret, setEngineClientSecret] = useLocalStorage(LOCAL_STORAGE_KEYS.engineClientSecret, '')

const [vaultUrl, setVaultUrl] = useLocalStorage(LOCAL_STORAGE_KEYS.vaultUrl, VAULT_URL)
const [vaultApiKey, setVaultApiKey] = useLocalStorage(LOCAL_STORAGE_KEYS.vaultApiKey, '')
const [vaultAdminApiKey, setVaultAdminApiKey] = useLocalStorage(LOCAL_STORAGE_KEYS.vaultAdminApiKey, '')
const [vaultClientId, setVaultClientId] = useLocalStorage(LOCAL_STORAGE_KEYS.vaultClientId, '')
const [vaultClientSecret, setVaultClientSecret] = useLocalStorage(LOCAL_STORAGE_KEYS.vaultClientSecret, '')

Expand Down Expand Up @@ -47,8 +47,8 @@ const useStore = () => {
setEngineClientSecret,
vaultUrl,
setVaultUrl,
vaultApiKey,
setVaultApiKey,
vaultAdminApiKey,
setVaultAdminApiKey,
vaultClientId,
setVaultClientId,
vaultClientSecret,
Expand Down
2 changes: 1 addition & 1 deletion apps/devtool/src/app/_lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const LOCAL_STORAGE_KEYS = {
engineClientSecret: 'narvalEngineClientSecret',
engineClientSigner: 'narvalEngineClientSigner',
vaultUrl: 'narvalVaultUrl',
vaultApiKey: 'narvalVaultApiKey',
vaultAdminApiKey: 'narvalVaultAdminApiKey',
vaultClientId: 'narvalVaultClientId',
vaultClientSecret: 'narvalVaultClientSecret',
entityDataStoreUrl: 'narvalEntityDataStoreUrl',
Expand Down
2 changes: 2 additions & 0 deletions apps/devtool/src/app/_lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export const classNames = (...classes: Array<string | undefined | null>) => {
const twMerge = extendTailwindMerge({ prefix: 'nv-' })
return twMerge(...classes)
}

export const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.toLowerCase().slice(1)
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
'use client'

import { faCheckCircle, faSpinner } from '@fortawesome/pro-regular-svg-icons'
import { faPlus, faSpinner } from '@fortawesome/pro-regular-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import axios from 'axios'
import { useState } from 'react'
import GreenCheckStatus from '../../_components/GreenCheckStatus'
import NarButton from '../../_design-system/NarButton'
import NarInput from '../../_design-system/NarInput'
import useAccountSignature from '../../_hooks/useAccountSignature'
Expand Down Expand Up @@ -33,12 +34,13 @@ const PolicyEngineConfig = () => {
policyDataStoreUrl,
policySignatureUrl
} = useStore()

const { jwk } = useAccountSignature()

const [isProcessing, setIsProcessing] = useState<boolean>(false)
const [isOnboarded, setIsOnboarded] = useState<boolean>(false)

const onboard = async () => {
const onboardClient = async () => {
if (!engineAdminApiKey || !jwk) return

setIsProcessing(true)
Expand All @@ -47,7 +49,6 @@ const PolicyEngineConfig = () => {
const { data: client } = await axios.post(
`${engineUrl}/clients`,
{
...(engineClientId && { clientId: engineClientId }),
entityDataStore: {
dataUrl: entityDataStoreUrl,
signatureUrl: entitySignatureUrl,
Expand All @@ -71,9 +72,8 @@ const PolicyEngineConfig = () => {
setEngineClientSigner(client.signer.publicKey)

setIsOnboarded(true)
setTimeout(() => {
setIsOnboarded(false)
}, 5000)

setTimeout(() => setIsOnboarded(false), 5000)
} catch (error) {
console.log(error)
}
Expand All @@ -83,36 +83,33 @@ const PolicyEngineConfig = () => {

return (
<div className="flex flex-col gap-10">
<div className="text-nv-2xl">Configuration</div>
<div className="flex items-center">
<div className="text-nv-2xl grow">Policy Engine</div>
<div className="flex items-center gap-4">
<GreenCheckStatus isChecked={isOnboarded} label={isOnboarded ? 'Client Onboarded!' : 'Onboarding...'} />
<NarButton
label={isProcessing ? 'Processing...' : 'Add client'}
leftIcon={
isProcessing ? (
<FontAwesomeIcon icon={faSpinner} spin />
) : (
<FontAwesomeIcon icon={faPlus} spin={isProcessing} />
)
}
onClick={onboardClient}
disabled={isProcessing}
/>
</div>
</div>
<div className="flex gap-20">
<div className="flex flex-col gap-6 w-1/3">
<NarInput label="Engine URL" value={engineUrl} onChange={setEngineUrl} />
<NarInput label="Admin API Key" value={engineAdminApiKey} onChange={setEngineAdminApiKey} />
<div className="flex flex-row-reverse">
{engineUrl && engineAdminApiKey && !engineClientId && (
<NarButton
label={isProcessing ? 'Processing...' : 'Onboard'}
rightIcon={isProcessing ? <FontAwesomeIcon icon={faSpinner} spin /> : undefined}
onClick={onboard}
disabled={isProcessing}
/>
)}
{isOnboarded && (
<div className="flex items-center gap-2">
<FontAwesomeIcon icon={faCheckCircle} className="text-nv-green-500" />
<div className="text-nv-white">Client Onboarded!</div>
</div>
)}
</div>
</div>
<div className="flex flex-col gap-6 w-2/3">
{engineClientSigner && (
<NarInput label="Client Signer" value={JSON.stringify(engineClientSigner)} onChange={() => null} disabled />
)}
{engineClientId && <NarInput label="Client ID" value={engineClientId} onChange={() => null} disabled />}
{engineClientSecret && (
<NarInput label="Client Secret" value={engineClientSecret} onChange={() => null} disabled />
)}
<NarInput label="Client Signer" value={JSON.stringify(engineClientSigner)} onChange={() => null} disabled />
<NarInput label="Client ID" value={engineClientId} onChange={() => null} disabled />
<NarInput label="Client Secret" value={engineClientSecret} onChange={() => null} disabled />
</div>
</div>
</div>
Expand Down
97 changes: 97 additions & 0 deletions apps/devtool/src/app/config/_components/VaultConfig.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
'use client'

import { faPlus, faSpinner } from '@fortawesome/pro-regular-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import axios from 'axios'
import { useState } from 'react'
import GreenCheckStatus from '../../_components/GreenCheckStatus'
import NarButton from '../../_design-system/NarButton'
import NarInput from '../../_design-system/NarInput'
import useStore from '../../_hooks/useStore'

const VaultConfig = () => {
const {
engineClientSigner,
setEngineClientSigner,
vaultUrl,
setVaultUrl,
vaultAdminApiKey,
setVaultAdminApiKey,
vaultClientId,
setVaultClientId,
vaultClientSecret,
setVaultClientSecret
} = useStore()

const [isProcessing, setIsProcessing] = useState<boolean>(false)
const [isOnboarded, setIsOnboarded] = useState<boolean>(false)

const onboardClient = async () => {
if (!vaultAdminApiKey) return

setIsProcessing(true)

try {
const { data: client } = await axios.post(
`${vaultUrl}/clients`,
{
...(vaultClientId && { clientId: vaultClientId }),
...(engineClientSigner && { engineJwk: engineClientSigner })
},
{
headers: {
'x-api-key': vaultAdminApiKey
}
}
)

setVaultClientId(client.clientId)
setVaultClientSecret(client.clientSecret)
setEngineClientSigner(client.engineJwk)

setIsOnboarded(true)

setTimeout(() => setIsOnboarded(false), 5000)
} catch (error) {
console.log(error)
}

setIsProcessing(false)
}

return (
<div className="flex flex-col gap-10">
<div className="flex items-center">
<div className="text-nv-2xl grow">Vault</div>
<div className="flex items-center gap-4">
<GreenCheckStatus isChecked={isOnboarded} label={isOnboarded ? 'Client Onboarded!' : 'Onboarding...'} />
<NarButton
label={isProcessing ? 'Processing...' : 'Add client'}
leftIcon={
isProcessing ? (
<FontAwesomeIcon icon={faSpinner} spin />
) : (
<FontAwesomeIcon icon={faPlus} spin={isProcessing} />
)
}
onClick={onboardClient}
disabled={isProcessing}
/>
</div>
</div>
<div className="flex gap-20">
<div className="flex flex-col gap-6 w-1/3">
<NarInput label="Vault URL" value={vaultUrl} onChange={setVaultUrl} />
<NarInput label="Admin API Key" value={vaultAdminApiKey} onChange={setVaultAdminApiKey} />
</div>
<div className="flex flex-col gap-6 w-2/3">
<NarInput label="Client Signer" value={JSON.stringify(engineClientSigner)} onChange={() => null} disabled />
<NarInput label="Client ID" value={vaultClientId} onChange={() => null} disabled />
<NarInput label="Client Secret" value={vaultClientSecret} onChange={() => null} disabled />
</div>
</div>
</div>
)
}

export default VaultConfig
11 changes: 11 additions & 0 deletions apps/devtool/src/app/config/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import PolicyEngineConfig from './_components/PolicyEngineConfig'
import VaultConfig from './_components/VaultConfig'

export default async function PolicyEngine() {
return (
<div className="flex flex-col gap-20">
<PolicyEngineConfig />
<VaultConfig />
</div>
)
}
31 changes: 31 additions & 0 deletions apps/devtool/src/app/data-store/_components/CodeEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'use client'

import { Editor } from '@monaco-editor/react'
import { FC, useRef } from 'react'

interface CodeEditorProps {
value: string | undefined
onChange: (value: string | undefined) => void
}

const CodeEditor: FC<CodeEditorProps> = ({ value, onChange }) => {
const editorRef = useRef<any>(null)
const monacoRef = useRef<any>(null)

return (
<div className="border-2 border-white rounded-xl p-4 w-full">
<Editor
height="70vh"
language="json"
value={value}
onChange={(value) => onChange(value)}
onMount={(editor, monaco) => {
editorRef.current = editor
monacoRef.current = monaco
}}
/>
</div>
)
}

export default CodeEditor
Loading

0 comments on commit a0699dd

Please sign in to comment.