66 type IntrospectionQuery ,
77} from 'graphql' ;
88import { toast } from 'sonner' ;
9+ import z from 'zod' ;
10+ import { asyncInterval } from '@/lib/utils' ;
911
1012export interface LaboratoryEndpointState {
1113 endpoint : string | null ;
@@ -20,6 +22,16 @@ export interface LaboratoryEndpointActions {
2022 restoreDefaultEndpoint : ( ) => void ;
2123}
2224
25+ const GraphQLResponseErrorSchema = z
26+ . object ( {
27+ errors : z . array (
28+ z . object ( {
29+ message : z . string ( ) ,
30+ } ) ,
31+ ) ,
32+ } )
33+ . strict ( ) ;
34+
2335export const useEndpoint = ( props : {
2436 defaultEndpoint ?: string | null ;
2537 onEndpointChange ?: ( endpoint : string | null ) => void ;
@@ -40,35 +52,87 @@ export const useEndpoint = (props: {
4052 return introspection ? buildClientSchema ( introspection ) : null ;
4153 } , [ introspection ] ) ;
4254
43- const fetchSchema = useCallback ( async ( ) => {
44- if ( endpoint === props . defaultEndpoint && props . defaultSchemaIntrospection ) {
45- setIntrospection ( props . defaultSchemaIntrospection ) ;
46- return ;
47- }
55+ const fetchSchema = useCallback (
56+ async ( signal ?: AbortSignal ) => {
57+ if ( endpoint === props . defaultEndpoint && props . defaultSchemaIntrospection ) {
58+ setIntrospection ( props . defaultSchemaIntrospection ) ;
59+ return ;
60+ }
4861
49- if ( ! endpoint ) {
50- setIntrospection ( null ) ;
51- return ;
52- }
62+ if ( ! endpoint ) {
63+ setIntrospection ( null ) ;
64+ return ;
65+ }
66+
67+ try {
68+ const response = await fetch ( endpoint , {
69+ signal,
70+ method : 'POST' ,
71+ body : JSON . stringify ( {
72+ query : getIntrospectionQuery ( ) ,
73+ } ) ,
74+ headers : {
75+ 'Content-Type' : 'application/json' ,
76+ } ,
77+ } ) . then ( r => r . json ( ) ) ;
78+
79+ const parsedResponse = GraphQLResponseErrorSchema . safeParse ( response ) ;
80+
81+ if ( parsedResponse . success ) {
82+ throw new Error ( parsedResponse . data . errors . map ( e => e . message ) . join ( '\n' ) ) ;
83+ }
84+
85+ if ( response . error && typeof response . error === 'string' ) {
86+ throw new Error ( response . error ) ;
87+ }
5388
54- try {
55- const response = await fetch ( endpoint , {
56- method : 'POST' ,
57- body : JSON . stringify ( {
58- query : getIntrospectionQuery ( ) ,
59- } ) ,
60- headers : {
61- 'Content-Type' : 'application/json' ,
62- } ,
63- } ) . then ( r => r . json ( ) ) ;
64-
65- setIntrospection ( response . data as IntrospectionQuery ) ;
66- } catch {
67- toast . error ( 'Failed to fetch schema' ) ;
68- setIntrospection ( null ) ;
89+ setIntrospection ( response . data as IntrospectionQuery ) ;
90+ } catch ( error : unknown ) {
91+ if (
92+ error &&
93+ typeof error === 'object' &&
94+ 'message' in error &&
95+ typeof error . message === 'string'
96+ ) {
97+ toast . error ( error . message ) ;
98+ } else {
99+ toast . error ( 'Failed to fetch schema' ) ;
100+ }
101+
102+ setIntrospection ( null ) ;
103+
104+ throw error ;
105+ }
106+ } ,
107+ [ endpoint ] ,
108+ ) ;
109+
110+ const shouldPollSchema = useMemo ( ( ) => {
111+ return endpoint !== props . defaultEndpoint || ! props . defaultSchemaIntrospection ;
112+ } , [ endpoint , props . defaultEndpoint , props . defaultSchemaIntrospection ] ) ;
113+
114+ useEffect ( ( ) => {
115+ if ( ! shouldPollSchema || ! endpoint ) {
69116 return ;
70117 }
71- } , [ endpoint ] ) ;
118+
119+ const intervalController = new AbortController ( ) ;
120+
121+ void asyncInterval (
122+ async ( ) => {
123+ try {
124+ await fetchSchema ( intervalController . signal ) ;
125+ } catch {
126+ intervalController . abort ( ) ;
127+ }
128+ } ,
129+ 5000 ,
130+ intervalController . signal ,
131+ ) ;
132+ return ( ) => {
133+ intervalController . abort ( ) ;
134+ } ;
135+ } , [ shouldPollSchema , fetchSchema ] ) ;
72136
73137 const restoreDefaultEndpoint = useCallback ( ( ) => {
74138 if ( props . defaultEndpoint ) {
@@ -77,10 +141,10 @@ export const useEndpoint = (props: {
77141 } , [ props . defaultEndpoint ] ) ;
78142
79143 useEffect ( ( ) => {
80- if ( endpoint ) {
144+ if ( endpoint && ! shouldPollSchema ) {
81145 void fetchSchema ( ) ;
82146 }
83- } , [ endpoint , fetchSchema ] ) ;
147+ } , [ endpoint , fetchSchema , shouldPollSchema ] ) ;
84148
85149 return {
86150 endpoint,
0 commit comments