Proto files off model

This commit is contained in:
Gregory Schier
2024-02-24 14:16:58 -08:00
parent 046a569ebe
commit 5bd2d0959a
16 changed files with 67 additions and 77 deletions

View File

@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
"query": "\n SELECT\n id, model, workspace_id, folder_id, created_at, updated_at, name, sort_priority,\n url, service, method, message, authentication_type,\n authentication AS \"authentication!: Json<HashMap<String, JsonValue>>\",\n proto_files AS \"proto_files!: sqlx::types::Json<Vec<String>>\",\n metadata AS \"metadata!: sqlx::types::Json<Vec<GrpcMetadataEntry>>\"\n FROM grpc_requests\n WHERE id = ?\n ",
"query": "\n SELECT\n id, model, workspace_id, folder_id, created_at, updated_at, name, sort_priority,\n url, service, method, message, authentication_type,\n authentication AS \"authentication!: Json<HashMap<String, JsonValue>>\",\n metadata AS \"metadata!: sqlx::types::Json<Vec<GrpcMetadataEntry>>\"\n FROM grpc_requests\n WHERE workspace_id = ?\n ",
"describe": {
"columns": [
{
@@ -73,14 +73,9 @@
"ordinal": 13,
"type_info": "Text"
},
{
"name": "proto_files!: sqlx::types::Json<Vec<String>>",
"ordinal": 14,
"type_info": "Text"
},
{
"name": "metadata!: sqlx::types::Json<Vec<GrpcMetadataEntry>>",
"ordinal": 15,
"ordinal": 14,
"type_info": "Text"
}
],
@@ -102,9 +97,8 @@
false,
true,
false,
false,
false
]
},
"hash": "697afa2c3072b7b01676be53d5857066724fc23c94cd279d074253d5581b6f2c"
"hash": "1821c2f60b9fa4514d58eb73b23e25ad683b80b9bd0c2944063190a0d0a19ee5"
}

View File

@@ -1,12 +0,0 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO grpc_requests (\n id, name, workspace_id, folder_id, sort_priority, url, service, method, message,\n proto_files, authentication_type, authentication, metadata\n )\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n updated_at = CURRENT_TIMESTAMP,\n name = excluded.name,\n folder_id = excluded.folder_id,\n sort_priority = excluded.sort_priority,\n url = excluded.url,\n service = excluded.service,\n method = excluded.method,\n message = excluded.message,\n proto_files = excluded.proto_files,\n authentication_type = excluded.authentication_type,\n authentication = excluded.authentication,\n metadata = excluded.metadata\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 13
},
"nullable": []
},
"hash": "38e8fd3b0959623322bf49cf6682a4ddeac667cf6e71b97bc7e122848ad1565f"
}

View File

@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO grpc_requests (\n id, name, workspace_id, folder_id, sort_priority, url, service, method, message,\n authentication_type, authentication, metadata\n )\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n updated_at = CURRENT_TIMESTAMP,\n name = excluded.name,\n folder_id = excluded.folder_id,\n sort_priority = excluded.sort_priority,\n url = excluded.url,\n service = excluded.service,\n method = excluded.method,\n message = excluded.message,\n authentication_type = excluded.authentication_type,\n authentication = excluded.authentication,\n metadata = excluded.metadata\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 12
},
"nullable": []
},
"hash": "467b87ad1209a4653b1dc8462d79236a655240c5b402fa9fd75c12ebd9bb6b86"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
"query": "\n SELECT\n id, model, workspace_id, folder_id, created_at, updated_at, name, sort_priority,\n url, service, method, message, authentication_type,\n authentication AS \"authentication!: Json<HashMap<String, JsonValue>>\",\n proto_files AS \"proto_files!: sqlx::types::Json<Vec<String>>\",\n metadata AS \"metadata!: sqlx::types::Json<Vec<GrpcMetadataEntry>>\"\n FROM grpc_requests\n WHERE workspace_id = ?\n ",
"query": "\n SELECT\n id, model, workspace_id, folder_id, created_at, updated_at, name, sort_priority,\n url, service, method, message, authentication_type,\n authentication AS \"authentication!: Json<HashMap<String, JsonValue>>\",\n metadata AS \"metadata!: sqlx::types::Json<Vec<GrpcMetadataEntry>>\"\n FROM grpc_requests\n WHERE id = ?\n ",
"describe": {
"columns": [
{
@@ -73,14 +73,9 @@
"ordinal": 13,
"type_info": "Text"
},
{
"name": "proto_files!: sqlx::types::Json<Vec<String>>",
"ordinal": 14,
"type_info": "Text"
},
{
"name": "metadata!: sqlx::types::Json<Vec<GrpcMetadataEntry>>",
"ordinal": 15,
"ordinal": 14,
"type_info": "Text"
}
],
@@ -102,9 +97,8 @@
false,
true,
false,
false,
false
]
},
"hash": "24a721700844cb0ea9c2585c4df796ec10188b559063a9093e432ae993154f31"
"hash": "e1cdba43bd938772631263966a9bee263923c387f4864917f36a04043bec4857"
}

View File

@@ -17,7 +17,6 @@ CREATE TABLE grpc_requests
service TEXT NULL,
method TEXT NULL,
message TEXT NOT NULL,
proto_files TEXT DEFAULT '[]' NOT NULL,
authentication TEXT DEFAULT '{}' NOT NULL,
authentication_type TEXT NULL,
metadata TEXT DEFAULT '[]' NOT NULL

View File

@@ -98,6 +98,7 @@ async fn migrate_db(app_handle: AppHandle, db: &Mutex<Pool<Sqlite>>) -> Result<(
#[tauri::command]
async fn cmd_grpc_reflect(
request_id: &str,
proto_files: Vec<String>,
window: Window,
grpc_handle: State<'_, Mutex<GrpcHandle>>,
) -> Result<Vec<ServiceDefinition>, String> {
@@ -105,15 +106,14 @@ async fn cmd_grpc_reflect(
.await
.map_err(|e| e.to_string())?;
let uri = safe_uri(&req.url).map_err(|e| e.to_string())?;
if req.proto_files.0.len() > 0 {
if proto_files.len() > 0 {
grpc_handle
.lock()
.await
.services_from_files(
&req.id,
&uri,
req.proto_files
.0
proto_files
.iter()
.map(|p| PathBuf::from_str(p).unwrap())
.collect(),
@@ -132,6 +132,7 @@ async fn cmd_grpc_reflect(
async fn cmd_grpc_go(
request_id: &str,
environment_id: Option<&str>,
proto_files: Vec<String>,
w: Window,
grpc_handle: State<'_, Mutex<GrpcHandle>>,
) -> Result<String, String> {
@@ -240,8 +241,7 @@ async fn cmd_grpc_go(
.connect(
&req.clone().id,
uri,
req.proto_files
.0
proto_files
.iter()
.map(|p| PathBuf::from_str(p).unwrap())
.collect(),

View File

@@ -213,7 +213,6 @@ pub struct GrpcRequest {
pub service: Option<String>,
pub method: Option<String>,
pub message: String,
pub proto_files: Json<Vec<String>>,
pub authentication_type: Option<String>,
pub authentication: Json<HashMap<String, JsonValue>>,
pub metadata: Json<Vec<GrpcMetadataEntry>>,
@@ -525,9 +524,9 @@ pub async fn upsert_grpc_request(
r#"
INSERT INTO grpc_requests (
id, name, workspace_id, folder_id, sort_priority, url, service, method, message,
proto_files, authentication_type, authentication, metadata
authentication_type, authentication, metadata
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT (id) DO UPDATE SET
updated_at = CURRENT_TIMESTAMP,
name = excluded.name,
@@ -537,7 +536,6 @@ pub async fn upsert_grpc_request(
service = excluded.service,
method = excluded.method,
message = excluded.message,
proto_files = excluded.proto_files,
authentication_type = excluded.authentication_type,
authentication = excluded.authentication,
metadata = excluded.metadata
@@ -551,7 +549,6 @@ pub async fn upsert_grpc_request(
request.service,
request.method,
request.message,
request.proto_files,
request.authentication_type,
request.authentication,
request.metadata,
@@ -577,7 +574,6 @@ pub async fn get_grpc_request(
id, model, workspace_id, folder_id, created_at, updated_at, name, sort_priority,
url, service, method, message, authentication_type,
authentication AS "authentication!: Json<HashMap<String, JsonValue>>",
proto_files AS "proto_files!: sqlx::types::Json<Vec<String>>",
metadata AS "metadata!: sqlx::types::Json<Vec<GrpcMetadataEntry>>"
FROM grpc_requests
WHERE id = ?
@@ -600,7 +596,6 @@ pub async fn list_grpc_requests(
id, model, workspace_id, folder_id, created_at, updated_at, name, sort_priority,
url, service, method, message, authentication_type,
authentication AS "authentication!: Json<HashMap<String, JsonValue>>",
proto_files AS "proto_files!: sqlx::types::Json<Vec<String>>",
metadata AS "metadata!: sqlx::types::Json<Vec<GrpcMetadataEntry>>"
FROM grpc_requests
WHERE workspace_id = ?

View File

@@ -5,6 +5,7 @@ import { useActiveRequest } from '../hooks/useActiveRequest';
import { useGrpc } from '../hooks/useGrpc';
import { useGrpcConnections } from '../hooks/useGrpcConnections';
import { useGrpcEvents } from '../hooks/useGrpcEvents';
import { useGrpcProtoFiles } from '../hooks/useGrpcProtoFiles';
import { useUpdateGrpcRequest } from '../hooks/useUpdateGrpcRequest';
import { Banner } from './core/Banner';
import { HotKeyList } from './core/HotKeyList';
@@ -22,7 +23,9 @@ export function GrpcConnectionLayout({ style }: Props) {
const connections = useGrpcConnections(activeRequest?.id ?? null);
const activeConnection = connections[0] ?? null;
const messages = useGrpcEvents(activeConnection?.id ?? null);
const grpc = useGrpc(activeRequest, activeConnection);
const protoFilesKv = useGrpcProtoFiles(activeRequest?.id ?? null);
const protoFiles = protoFilesKv.value ?? [];
const grpc = useGrpc(activeRequest, activeConnection, protoFiles);
const services = grpc.reflect.data ?? null;
useEffect(() => {
@@ -79,6 +82,7 @@ export function GrpcConnectionLayout({ style }: Props) {
<GrpcConnectionSetupPane
style={style}
activeRequest={activeRequest}
protoFiles={protoFiles}
methodType={methodType}
onGo={grpc.go.mutate}
onCommit={grpc.commit.mutate}

View File

@@ -27,6 +27,7 @@ interface Props {
style?: CSSProperties;
className?: string;
activeRequest: GrpcRequest;
protoFiles: string[];
reflectionError?: string;
reflectionLoading?: boolean;
methodType:
@@ -50,6 +51,7 @@ export function GrpcConnectionSetupPane({
services,
methodType,
activeRequest,
protoFiles,
reflectionError,
reflectionLoading,
onGo,
@@ -273,6 +275,7 @@ export function GrpcConnectionSetupPane({
reflectionError={reflectionError}
reflectionLoading={reflectionLoading}
request={activeRequest}
protoFiles={protoFiles}
/>
</TabContent>
<TabContent value="auth">

View File

@@ -21,6 +21,7 @@ type Props = Pick<EditorProps, 'heightMode' | 'onChange' | 'className'> & {
reflectionError?: string;
reflectionLoading?: boolean;
request: GrpcRequest;
protoFiles: string[];
};
export function GrpcEditor({
@@ -28,6 +29,7 @@ export function GrpcEditor({
reflectionError,
reflectionLoading,
request,
protoFiles,
...extraEditorProps
}: Props) {
const editorViewRef = useRef<EditorView>(null);
@@ -149,9 +151,9 @@ export function GrpcEditor({
? 'Select Proto Files'
: reflectionError
? 'Server Error'
: request.protoFiles.length > 0
? count('File', request.protoFiles.length)
: services != null && request.protoFiles.length === 0
: protoFiles.length > 0
? count('File', protoFiles.length)
: services != null && protoFiles.length === 0
? 'Schema Detected'
: 'Select Schema'}
</Button>

View File

@@ -1,7 +1,7 @@
import { open } from '@tauri-apps/api/dialog';
import { useGrpc } from '../hooks/useGrpc';
import { useGrpcProtoFiles } from '../hooks/useGrpcProtoFiles';
import { useGrpcRequest } from '../hooks/useGrpcRequest';
import { useUpdateGrpcRequest } from '../hooks/useUpdateGrpcRequest';
import { count } from '../lib/pluralize';
import { Banner } from './core/Banner';
import { Button } from './core/Button';
@@ -18,10 +18,11 @@ interface Props {
export function GrpcProtoSelection({ requestId }: Props) {
const request = useGrpcRequest(requestId);
const grpc = useGrpc(request, null);
const updateRequest = useUpdateGrpcRequest(request?.id ?? null);
const protoFilesKv = useGrpcProtoFiles(requestId);
const protoFiles = protoFilesKv.value ?? [];
const grpc = useGrpc(request, null, protoFiles);
const services = grpc.reflect.data;
const serverReflection = request?.protoFiles.length === 0 && services != null;
const serverReflection = protoFiles.length === 0 && services != null;
let reflectError = grpc.reflect.error ?? null;
const reflectionUnimplemented = `${reflectError}`.match(/unimplemented/i);
@@ -47,8 +48,8 @@ export function GrpcProtoSelection({ requestId }: Props) {
filters: [{ name: 'Proto Files', extensions: ['proto'] }],
});
if (files == null || typeof files === 'string') return;
const newFiles = files.filter((f) => !request.protoFiles.includes(f));
await updateRequest.mutateAsync({ protoFiles: [...request.protoFiles, ...newFiles] });
const newFiles = files.filter((f) => !protoFiles.includes(f));
await protoFilesKv.set([...protoFiles, ...newFiles]);
await grpc.reflect.refetch();
}}
>
@@ -99,7 +100,7 @@ export function GrpcProtoSelection({ requestId }: Props) {
</Banner>
)}
{request.protoFiles.length > 0 && (
{protoFiles.length > 0 && (
<table className="w-full divide-y">
<thead>
<tr>
@@ -110,7 +111,7 @@ export function GrpcProtoSelection({ requestId }: Props) {
</tr>
</thead>
<tbody className="divide-y">
{request.protoFiles.map((f, i) => (
{protoFiles.map((f, i) => (
<tr key={f + i} className="group">
<td className="pl-1 text-sm font-mono">{f.split('/').pop()}</td>
<td className="w-0 py-0.5">
@@ -120,9 +121,7 @@ export function GrpcProtoSelection({ requestId }: Props) {
icon="trash"
className="ml-auto opacity-30 transition-opacity group-hover:opacity-100"
onClick={async () => {
await updateRequest.mutateAsync({
protoFiles: request.protoFiles.filter((p) => p !== f),
});
await protoFilesKv.set(protoFiles.filter((p) => p !== f));
grpc.reflect.remove();
}}
/>
@@ -133,7 +132,7 @@ export function GrpcProtoSelection({ requestId }: Props) {
</table>
)}
{reflectError && <FormattedError>{reflectError}</FormattedError>}
{reflectionUnimplemented && request.protoFiles.length === 0 && (
{reflectionUnimplemented && protoFiles.length === 0 && (
<Banner>
<InlineCode>{request.url}</InlineCode> doesn&apos;t implement{' '}
<Link href="https://github.com/grpc/grpc/blob/9aa3c5835a4ed6afae9455b63ed45c761d695bca/doc/server-reflection.md">

View File

@@ -11,12 +11,16 @@ export interface ReflectResponseService {
methods: { name: string; schema: string; serverStreaming: boolean; clientStreaming: boolean }[];
}
export function useGrpc(req: GrpcRequest | null, conn: GrpcConnection | null) {
export function useGrpc(
req: GrpcRequest | null,
conn: GrpcConnection | null,
protoFiles: string[],
) {
const requestId = req?.id ?? 'n/a';
const environmentId = useActiveEnvironmentId();
const go = useMutation<void, string>({
mutationFn: async () => await invoke('cmd_grpc_go', { requestId, environmentId }),
mutationFn: async () => await invoke('cmd_grpc_go', { requestId, environmentId, protoFiles }),
});
const send = useMutation({
@@ -35,13 +39,13 @@ export function useGrpc(req: GrpcRequest | null, conn: GrpcConnection | null) {
});
const debouncedUrl = useDebouncedValue<string>(req?.url ?? 'n/a', 1000);
const reflect = useQuery<ReflectResponseService[] | null, string>({
const reflect = useQuery<ReflectResponseService[], string>({
enabled: req != null,
queryKey: ['grpc_reflect', req?.id ?? 'n/a', debouncedUrl],
refetchOnWindowFocus: false,
queryFn: async () => {
return (await minPromiseMillis(
invoke('cmd_grpc_reflect', { requestId }),
invoke('cmd_grpc_reflect', { requestId, protoFiles }),
300,
)) as ReflectResponseService[];
},

View File

@@ -0,0 +1,10 @@
import { NAMESPACE_GLOBAL } from '../lib/keyValueStore';
import { useKeyValue } from './useKeyValue';
export function useGrpcProtoFiles(activeRequestId: string | null) {
return useKeyValue<string[]>({
namespace: NAMESPACE_GLOBAL,
key: ['proto_files', activeRequestId ?? 'n/a'],
defaultValue: [],
});
}

View File

@@ -1,12 +0,0 @@
import { NAMESPACE_APP } from '../lib/keyValueStore';
import { useKeyValue } from './useKeyValue';
export function useUpdateMode() {
const kv = useKeyValue<'stable' | 'beta'>({
namespace: NAMESPACE_APP,
key: 'update_mode',
defaultValue: 'stable',
});
return [kv.value, kv.set] as const;
}

View File

@@ -1,7 +1,6 @@
import { invoke } from '@tauri-apps/api';
import type { KeyValue } from './models';
export const NAMESPACE_APP = 'app';
export const NAMESPACE_GLOBAL = 'global';
export const NAMESPACE_NO_SYNC = 'no_sync';

View File

@@ -121,7 +121,6 @@ export interface GrpcRequest extends BaseModel {
service: string | null;
method: string | null;
message: string;
protoFiles: string[];
authentication: Record<string, string | number | boolean | null | undefined>;
authenticationType: string | null;
metadata: GrpcMetadataEntry[];