I'm having trouble with GraphQL Subscriptions in React-Apollo. The issue is that when I create a subscription it gets stuck on loading forever. Despite that, the GraphQL Subscriptions work in gql-playground so it seems like a front-end issue. I've followed the documentation closely to match it, but the subscription still returns undefined in the console and is loading.
Here is how the WebSockets with Subscriptions is set up in index.js
// Link for HTTP Requests
const httpLink = new HttpLink({
uri: 'http://localhost:8080/api'
});
// Link for Websocket Links
const wsLink = new WebSocketLink({
uri: 'ws://localhost:8080/api',
options: {
reconnect: true
}
});
// Split Function takes the operation to execute, and reuturns the Websocket Link or HTTP Link depending on a boolean value
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink,
);
cosnt
const client = new ApolloClient({
connectToDevTools: true,
cache: new InMemoryCache(),
link: splitLink,
})
ReactDOM.render(
<React.StrictMode>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</React.StrictMode>,
document.getElementById('root')
);
and this is the WristbandSubscriptions component I created to test the subscriptions
const WristbandSubscriptions = () => {
const NEW_WRISTBAND_ADDED = gql`
subscription {
newWristbandAdded {
id
tic
active
name
dateOfBirth
onOxygen
pregnant
child
key
department
}
}`
;
const { data, loading, error } = useSubscription(NEW_WRISTBAND_ADDED);
useEffect(()=> {
console.log(data);
},[data])
console.log(error);
console.log("Loading: " + loading);
return (
<div>
<h1>{data}</h1>
</div>
)
}
Related
Hi I am wondering how I get this behavior:
There is a Button which starts the stream of a specific DIV (already worked using HTTP).
The Stream should be encrypted using HTTPS on localhost.
I changed the connection to HTTPS, created a key and a certificate with this command:openssl req nodes new x509 keyout server.key out server.cert. In the Chrome Dev Tool / Network Section I receive this:
Chrome Screenshot
BUT: the part on the server where I log that a user connected successfully isn't executed and I can't figure out why.
my react component:
import React, { useEffect, useState, useRef } from 'react';
import html2canvas from 'html2canvas';
import io from 'socket.io-client';
const socket = io('https://localhost:8080', {
transports: ['websocket'],
cors: {
origin: 'http://localhost:3000',
},
});
function Stream() {
const [message, setMessage] = useState("Streaming: OFF");
const [streaming, setStreaming] = useState(false);
const sectionRef = useRef(null);
useEffect(() => {
if (!streaming) return;
const intervalId = setInterval(() => {
html2canvas(sectionRef.current).then(canvas => {
socket.emit("streaming", canvas.toDataURL("image/webp"));
});
}, 40);
return () => {
clearInterval(intervalId);
};
}, [streaming]);
const startStream = () => {
setMessage("Streaming: ON");
setStreaming(true);
};
const stopStream = () => {
setMessage("Streaming: OFF");
setStreaming(false);
};
return (
<div>
<h2>Anchor:</h2>
<button onClick={startStream} disabled={streaming}>Start streaming</button>
<button onClick={stopStream} disabled={!streaming}>Stop streaming</button>
<p>{message}</p>
<div ref={sectionRef}>
{/* Replace this with the section of the website you want to stream */}
<p>This is the section of the website that will be streamed</p>
</div>
</div>
);
}
export default Stream;
and the server looks like this:
const https = require('https');
const fs = require('fs');
const cors2 = require('cors');
const options = {
key: fs.readFileSync(\__dirname + '/server.key'),
cert: fs.readFileSync(\__dirname + '/server.cert')
};
const server = https.createServer(options);
const io = require('socket.io')(server);
io.use(cors2({
origin: "\*",
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
allowedHeaders: "Content-Type,Authorization"
}));
io.on('connection', (socket) =\> {
console.log('a user connected');
socket.on('streaming', (data) =\> {
console.log("received streaming data: ", data);
io.emit('streaming', data);
});
});
server.listen(8080, () =\> console.log('listening on '));
changed browser to Firefox, activated chrome://flags/#allow-insecure-localhost
I'm trying to get subscription data. The server gives the data if you look through the Explorer.
Client:
const httpLink = createHttpLink({
uri
});
const wsLink =
typeof window !== "undefined"
? new GraphQLWsLink(
createClient({
url,
on: {
connected: () => console.log("Connected client!"),
closed: () => console.log("Closed ws-connection!"),
},
})
)
: null;
const splitLink =
typeof window !== "undefined" && wsLink != null
? split(
({ query }) => {
const def = getMainDefinition(query);
return (
def.kind === "OperationDefinition" &&
def.operation === "subscription"
);
},
wsLink,
httpLink
)
: httpLink;
const authLink = setContext((_, { headers }) => {
const {token} = useTokenFromCookie();
return {
headers: {
...headers,
authorization: token ? `Bearer ${token()}` : "",
}
}
});
const client = new ApolloClient({
link: authLink.concat(splitLink),
cache: new InMemoryCache({addTypename: false}),
defaultOptions: {
mutate: { errorPolicy: 'all' },
},
});
gql:
subscription ReadRegisterData($equipmentId: Int!, $addressRegistry: Int!) {
readRegisterData(equipment_id: $equipmentId, address_registry: $addressRegistry) {
equipment_id
address_registry
type_of
data_from_controller
}
}
Hook:
const useSubscriptionReadRegisterData = (equipment_ip:number, address_registry: number) => {
const { data, error, loading} = useSubscription(READ_REGISTER_DATA, {
variables: {
equipmentIp: equipment_ip,
addressRegistry: address_registry
}
});
console.log("data", data)
const dataRegisterSubscription = (data) ? data.readRegisterData : null;
return { dataRegisterSubscription, error, loading }
}
export default useSubscriptionReadRegisterData;
In the console writes:
The connection is established, then immediately the connection is closed
WS connection status 101
When you start listening to a subscription through Explorer, on the server, when outputting data to the log, you can see that there is 1 Listener. When you run in the application, it does not show any Listener
I got undefined on console.log below
loading => true
error => undefined
data => undefined
This is my react-native code
const {error, loading, data} = useQuery(gql`
{
fetchLatestMessages(channelId: "General") {
messageId
text
datetime
userId
}
}
`);
console.log('loading:', loading);
console.log('error:', error);
console.log('data:', data);
This is My playground it's work fine but empty data.
// Here my setup code and I was try to use fetch for make sure API is fine
const url = 'https://angular-test-backend-yc4c5cvnnq-an.a.run.app/graphql';
const client = new ApolloClient({
uri: url,
cache: new InMemoryCache(),
});
const App = () => {
React.useEffect(() => {
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({query}),
})
.then(response => response.json())
.then(responseData => console.log('responseData:', responseData));
}, []);
return (
<ApolloProvider client={client}>
<Root />
</ApolloProvider>
);
};
I try to query using fetch api it's work fine I got a response.
Need help to using useQuery instead of fetch.
Thank you.
I am currently using node js to publish topics to Event Grid, and subscribe to topics through Event Grid. Using the event grid API on https://learn.microsoft.com/en-us/rest/api/eventgrid/ I get an error where I do not have authorization to perform action when creating a subscription. I have created a topic and have access permission to access my Azure account therefore I am confused why I get this rest error.
My code:
const { ClientSecretCredential } = require("#azure/identity");
const { SystemTopicEventSubscriptions, EventGridManagementClientContext, DomainTopics, EventSubscriptions } = require("#azure/arm-eventgrid");
const subscriptionId = "idea number";
const resourceGroupName = "eventgrid-dev";
const domainName = "test-domain";
let tenantId = "idea number";
let clientSecret = "idea number";
let clientId = "idea number";
const firstCredential = new ClientSecretCredential(tenantId, clientId, clientSecret);
//const client = new EventGridManagementClient(firstCredential, subscriptionId);
const clientContext = new EventGridManagementClientContext(firstCredential, subscriptionId);
// Topics
let domainTopics = new DomainTopics(clientContext);
domainTopics.beginCreateOrUpdate(resourceGroupName, domainName, "test-topic")
.then(result => {
console.log("result");
console.log(result);
})
.catch(error => {
console.log("Error");
console.log(error);
})
let subscription = new EventSubscriptions(clientContext);
subscription.beginCreateOrUpdate("/subscriptions/subscriptionId/resourceGroups/eventgrid-dev", "test-subscription",{topic: "test-topic"})
.then(result => {
console.log("result");
console.log(result);
})
.catch(error => {
console.log("Error");
console.log(error);
})
Output:
Error
RestError: The client 'subscriptionID' with object id 'subscriptionID' does not have authorization to perform action 'Microsoft.EventGrid/eventSubscriptions/Microsoft.EventGrid/test-subscription/write' over scope '/subscriptions/subscriptionID/resourceGroups/eventgrid-dev/providers/Microsoft.EventGrid/eventSubscriptions/providers/Microsoft.EventGrid/eventSubscriptions' or the scope is invalid. If access was recently granted, please refresh your credentials.
Thank you for the help!
In the case of existing an event grid domain, use the following code for creating an event grid subscription on the requested topic. Note, that the topic is created automatically during its first subscription:
let subscription = new EventSubscriptions(clientContext);
const scope = '/subscriptions/' + subscriptionId + '/resourceGroups/' + resourceGroupName + '/providers/Microsoft.EventGrid/domains/' + domainName + '/topics/test-topic';
const test_webhookEndpointUrl = ' ... ';
subscription.beginCreateOrUpdate(scope, "test-subscription",
{
destination: {
endpointType: "WebHook",
endpointUrl: test_webhookEndpointUrl
}
}
).then(result => {
console.log("result");
console.log(result);
})
.catch(error => {
console.log("Error");
console.log(error);
})
My bot is working fine while calling Speech Services using an authorization token + region pair.
Code snipped below.
webSpeechPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory({ authorizationToken, region });
However, the following warning message shows up on the browser:
botframework-webchat: "authorizationToken", "region", and "subscriptionKey" are deprecated and will be removed on or after 2020-12-17. Please use "credentials" instead.
How can migrate my authentication code to the new method?
Code samples are appreciated. Thx
This is the code I use. I run a server locally for calling the speech API that returns the token (and region).
In the Web Chat code, you simply need to pass the returned token and region in to createCognitiveServicesSpeechServicesPonyfillFactory(). Your code doesn't need to match mine, specifically. It is enough to do something like:
const webSpeechPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory( {
credentials: {
authorizationToken: authorizationToken,
region: region
}
} );
or, depending how you structure your credentials object,
{ credentials: credentials }
Sample code:
const path = require('path');
const restify = require('restify');
const request = require('request');
const bodyParser = require('body-parser');
const corsMiddleware = require('restify-cors-middleware');
const cors = corsMiddleware({
origins: ['*']
});
const ENV_FILE = path.join(__dirname, '.env');
require('dotenv').config({ path: ENV_FILE });
// Create HTTP server.
const server = restify.createServer();
server.pre(cors.preflight);
server.use(cors.actual);
server.use(bodyParser.json({
extended: false
}));
server.listen(process.env.port || process.env.PORT || 3500, function() {
console.log(`\n${ server.dl_name } listening to ${ server.url }.`);
});
server.post('/speechservices/token', async (req, res) => {
const options = {
method: 'POST',
uri: `https://${ process.env.SPEECH_SERVICES_REGION }.api.cognitive.microsoft.com/sts/v1.0/issueToken`,
headers: {
'Ocp-Apim-Subscription-Key': process.env.SPEECH_SERVICES_SUBSCRIPTION_KEY
}
};
request.post(options, (error, response, body) => {
if (!error && response.statusCode < 300) {
body = { region: process.env.SPEECH_SERVICES_REGION, authorizationToken: body };
res.send({
authorizationToken: body.authorizationToken,
region: body.region
});
console.log(`Someone requested a speech token...(${ response.statusCode })`);
} else if (response.statusCode >= 400 && response.statusCode < 500) {
res.send(response.statusCode);
} else if (response.statusCode >= 500) {
res.status(response.statusCode);
res.send('Call to retrieve token failed');
}
});
});
const getSpeechToken = async (credentials = {}) => {
const response = await fetch( `http://localhost:3500/speechservices/token`, {
method: 'POST',
} );
if ( response.status === 200 ) {
const { authorizationToken, region } = await response.json();
credentials['authorizationToken'] = authorizationToken;
credentials['region'] = region;
return credentials;
} else {
console.log('error')
}
}
const webSpeechPonyfillFactory = await window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory( {
credentials: await getSpeechToken()
} );
render(
<div>
<ReactWebChat
directLine={directLine}
selectVoice={( voices, activity ) =>
activity.locale === 'en-US' ?
voices.find( ( { name } ) => /KatjaNeural/iu.test( name ) )
:
voices.find( ( { name } ) => /KatjaNeural/iu.test( name ) )
|| voices.find( ( { name } ) => /Apollo/iu.test( name ) )}
webSpeechPonyfillFactory={webSpeechPonyfillFactory}
/>
</div>,
document.getElementById( 'webchat' )
);
Hope of help!