Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7b68581

Browse files
iamfaranraheeliftikhar5
authored andcommitted
setup frontend for ssehttpquery
1 parent188f9cb commit7b68581

File tree

3 files changed

+390
-244
lines changed

3 files changed

+390
-244
lines changed
Lines changed: 29 additions & 242 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1+
// SSEHTTPQUERY.tsx
12
import{Dropdown,ValueFromOption}from"components/Dropdown";
23
import{QueryConfigItemWrapper,QueryConfigLabel,QueryConfigWrapper}from"components/query";
34
import{valueComp,withDefault}from"comps/generators";
45
import{trans}from"i18n";
56
import{includes}from"lodash";
67
import{CompAction,MultiBaseComp}from"lowcoder-core";
78
import{keyValueListControl}from"../../controls/keyValueListControl";
8-
import{ParamsJsonControl,ParamsStringControl,ParamsControlType}from"../../controls/paramsControl";
9+
import{ParamsJsonControl,ParamsStringControl}from"../../controls/paramsControl";
910
import{withTypeAndChildrenAbstract}from"../../generators/withType";
10-
import{QueryResult}from"../queryComp";
11-
import{QUERY_EXECUTION_ERROR,QUERY_EXECUTION_OK}from"constants/queryConstants";
12-
import{JSONValue}from"util/jsonTypes";
13-
import{FunctionProperty}from"../queryCompUtils";
11+
import{toSseQueryView}from"../queryCompUtils";
1412
import{
1513
HttpHeaderPropertyView,
1614
HttpParametersPropertyView,
@@ -52,7 +50,9 @@ const CommandMap = {
5250
constchildrenMap={
5351
httpMethod:valueComp<HttpMethodValue>("GET"),
5452
path:ParamsStringControl,
55-
headers:withDefault(keyValueListControl(),[{key:"",value:""}]),
53+
headers:withDefault(keyValueListControl(),[
54+
{key:"Accept",value:"text/event-stream"}
55+
]),
5656
params:withDefault(keyValueListControl(),[{key:"",value:""}]),
5757
bodyFormData:withDefault(
5858
keyValueListControl(true,[
@@ -61,6 +61,8 @@ const childrenMap = {
6161
]asconst),
6262
[{key:"",value:"",type:"text"}]
6363
),
64+
// Add SSE-specific configuration
65+
streamingEnabled:valueComp<boolean>(true),
6466
};
6567

6668
constSseHttpTmpQuery=withTypeAndChildrenAbstract(
@@ -72,9 +74,6 @@ const SseHttpTmpQuery = withTypeAndChildrenAbstract(
7274
);
7375

7476
exportclassSseHttpQueryextendsSseHttpTmpQuery{
75-
privateeventSource:EventSource|undefined;
76-
privatecontroller:AbortController|undefined;
77-
7877
isWrite(action:CompAction){
7978
return(
8079
action.path.includes("httpMethod")&&"value"inaction&&!includes(["GET"],action.value)
@@ -89,241 +88,13 @@ export class SseHttpQuery extends SseHttpTmpQuery {
8988
...children.bodyFormData.getQueryParams(),
9089
...children.path.getQueryParams(),
9190
...children.body.getQueryParams(),
91+
// Add streaming flag to params
92+
{key:"_streaming",value:()=>"true"},
93+
{key:"_streamingEnabled",value:()=>children.streamingEnabled.getView()}
9294
];
9395

94-
returnthis.createStreamingQueryView(params);
95-
}
96-
97-
privatecreateStreamingQueryView(params:FunctionProperty[]){
98-
returnasync(props:{
99-
queryId:string;
100-
applicationId:string;
101-
applicationPath:string[];
102-
args?:Record<string,unknown>;
103-
variables?:any;
104-
timeout:InstanceType<ParamsControlType>;
105-
callback?:(result:QueryResult)=>void;
106-
}):Promise<QueryResult>=>{
107-
108-
try{
109-
consttimer=performance.now();
110-
111-
// Process parameters like toQueryView does
112-
constprocessedParams=this.processParameters(params,props);
113-
114-
// Build request from processed parameters
115-
const{ url, headers, method, body}=this.buildRequestFromParams(processedParams,props.args);
116-
117-
// Execute streaming logic
118-
if(method==="GET"){
119-
returnthis.handleEventSource(url,headers,props,timer);
120-
}else{
121-
returnthis.handleStreamingFetch(url,headers,method,body,props,timer);
122-
}
123-
124-
}catch(error){
125-
returnthis.createErrorResponse((errorasError).message);
126-
}
127-
};
128-
}
129-
130-
privateprocessParameters(params:FunctionProperty[],props:any){
131-
letmappedVariables:Array<{key:string,value:string}>=[];
132-
Object.keys(props.variables||{})
133-
.filter(k=>k!=="$queryName")
134-
.forEach(key=>{
135-
constvalue=Object.hasOwn(props.variables[key],'value') ?props.variables[key].value :props.variables[key];
136-
mappedVariables.push({
137-
key:`${key}.value`,
138-
value:value||""
139-
});
140-
});
141-
142-
return[
143-
...params.filter(param=>{
144-
return!mappedVariables.map(v=>v.key).includes(param.key);
145-
}).map(({ key, value})=>({ key,value:value(props.args)})),
146-
...Object.entries(props.timeout.getView()).map(([key,value])=>({
147-
key,
148-
value:(valueasany)(props.args),
149-
})),
150-
...mappedVariables,
151-
];
152-
}
153-
154-
privatebuildRequestFromParams(processedParams:Array<{key:string,value:any}>,args:Record<string,unknown>={}){
155-
// Hardcoded values from the screenshot for testing
156-
consturl="http://localhost:11434/api/generate";
157-
constheaders={
158-
"Content-Type":"application/json",
159-
"Accept":"text/event-stream"
160-
};
161-
constmethod="POST";
162-
constbody=JSON.stringify({
163-
"model":"gemma3",
164-
"prompt":"Tell me a short story about a robot",
165-
"stream":true
166-
});
167-
168-
console.log("Hardcoded request:",{ url, headers, method, body});
169-
170-
return{ url, headers, method, body};
171-
}
172-
173-
privateasynchandleEventSource(
174-
url:string,
175-
headers:Record<string,string>,
176-
props:any,
177-
timer:number
178-
):Promise<QueryResult>{
179-
returnnewPromise((resolve,reject)=>{
180-
// Clean up any existing connection
181-
this.cleanup();
182-
183-
this.eventSource=newEventSource(url);
184-
185-
this.eventSource.onopen=()=>{
186-
resolve(this.createSuccessResponse("SSE connection established",timer));
187-
};
188-
189-
this.eventSource.onmessage=(event)=>{
190-
try{
191-
constdata=JSON.parse(event.data);
192-
props.callback?.(this.createSuccessResponse(data));
193-
}catch(error){
194-
// Handle non-JSON data
195-
props.callback?.(this.createSuccessResponse(event.data));
196-
}
197-
};
198-
199-
this.eventSource.onerror=(error)=>{
200-
this.cleanup();
201-
reject(this.createErrorResponse("SSE connection error"));
202-
};
203-
});
204-
}
205-
206-
privateasynchandleStreamingFetch(
207-
url:string,
208-
headers:Record<string,string>,
209-
method:string,
210-
body:string|FormData|undefined,
211-
props:any,
212-
timer:number
213-
):Promise<QueryResult>{
214-
// Clean up any existing connection
215-
this.cleanup();
216-
217-
this.controller=newAbortController();
218-
219-
constresponse=awaitfetch(url,{
220-
method,
221-
headers:{
222-
...headers,
223-
'Accept':'text/event-stream',
224-
},
225-
body,
226-
signal:this.controller.signal,
227-
});
228-
229-
if(!response.ok){
230-
thrownewError(`HTTP${response.status}:${response.statusText}`);
231-
}
232-
233-
// Handle streaming response
234-
constreader=response.body?.getReader();
235-
constdecoder=newTextDecoder();
236-
237-
if(!reader){
238-
thrownewError("No readable stream available");
239-
}
240-
241-
// Process stream in background
242-
this.processStream(reader,decoder,props.callback);
243-
244-
returnthis.createSuccessResponse("Stream connection established",timer);
245-
}
246-
247-
privateasyncprocessStream(
248-
reader:ReadableStreamDefaultReader<Uint8Array>,
249-
decoder:TextDecoder,
250-
callback?:(result:QueryResult)=>void
251-
){
252-
letbuffer='';
253-
254-
try{
255-
while(true){
256-
const{ done, value}=awaitreader.read();
257-
258-
if(done)break;
259-
260-
buffer+=decoder.decode(value,{stream:true});
261-
262-
// Process complete JSON objects or SSE events
263-
constlines=buffer.split('\n');
264-
buffer=lines.pop()||'';
265-
266-
for(constlineoflines){
267-
if(line.trim()){
268-
try{
269-
// Handle SSE format: data: {...}
270-
letjsonData=line.trim();
271-
if(jsonData.startsWith('data: ')){
272-
jsonData=jsonData.substring(6);
273-
}
274-
275-
// Skip SSE control messages
276-
if(jsonData==='[DONE]'||jsonData.startsWith('event:')||jsonData.startsWith('id:')){
277-
continue;
278-
}
279-
280-
constdata=JSON.parse(jsonData);
281-
callback?.(this.createSuccessResponse(data));
282-
}catch(error){
283-
// Handle non-JSON lines or plain text
284-
if(line.trim()!==''){
285-
callback?.(this.createSuccessResponse(line.trim()));
286-
}
287-
}
288-
}
289-
}
290-
}
291-
}catch(error:any){
292-
if(error.name!=='AbortError'){
293-
callback?.(this.createErrorResponse((errorasError).message));
294-
}
295-
}finally{
296-
reader.releaseLock();
297-
}
298-
}
299-
300-
privatecreateSuccessResponse(data:JSONValue,runTime?:number):QueryResult{
301-
return{
302-
data,
303-
runTime:runTime||0,
304-
success:true,
305-
code:QUERY_EXECUTION_OK,
306-
};
307-
}
308-
309-
privatecreateErrorResponse(message:string):QueryResult{
310-
return{
311-
message,
312-
data:"",
313-
success:false,
314-
code:QUERY_EXECUTION_ERROR,
315-
};
316-
}
317-
318-
publiccleanup(){
319-
if(this.eventSource){
320-
this.eventSource.close();
321-
this.eventSource=undefined;
322-
}
323-
if(this.controller){
324-
this.controller.abort();
325-
this.controller=undefined;
326-
}
96+
// Use SSE-specific query view
97+
returntoSseQueryView(params);
32798
}
32899

329100
propertyView(props:{
@@ -410,6 +181,13 @@ const SseHttpQueryPropertyView = (props: {
410181
letheaders=children.headers
411182
.toJsonValue()
412183
.filter((header)=>header.key!==ContentTypeKey);
184+
185+
// Always ensure Accept: text/event-stream for SSE
186+
consthasAcceptHeader=headers.some(h=>h.key==="Accept");
187+
if(!hasAcceptHeader){
188+
headers.push({key:"Accept",value:"text/event-stream"});
189+
}
190+
413191
if(value!=="none"){
414192
headers=[
415193
{
@@ -430,6 +208,15 @@ const SseHttpQueryPropertyView = (props: {
430208
<QueryConfigLabel/>
431209
<QueryConfigItemWrapper>{showBodyConfig(children)}</QueryConfigItemWrapper>
432210
</QueryConfigWrapper>
211+
212+
<QueryConfigWrapper>
213+
<QueryConfigLabel>Streaming Options</QueryConfigLabel>
214+
<QueryConfigItemWrapper>
215+
<divstyle={{fontSize:"13px",color:"#8B8FA3"}}>
216+
This query will establish a Server-Sent Events connection for real-time data streaming.
217+
</div>
218+
</QueryConfigItemWrapper>
219+
</QueryConfigWrapper>
433220
</>
434221
);
435222
};

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp