Apollo Websocket repeatedly attempts to connect over and over when updating context - go

I'm using Apollo on my frontend to manage queries and GQLgen on the backend to handle resolvers.
I've noticed this only occurs if webSocketInit returns a new ctx value. If I return the original ctx, everything works as expected.
//main.go
srv.AddTransport(&transport.Websocket{
Upgrader: websocket.Upgrader{
ReadBufferSize: 2048,
WriteBufferSize: 2048,
// Resolve cross-domain problems
CheckOrigin: func(r *http.Request) bool {
return true
},
},
InitFunc: func(ctx context.Context, initPayload transport.InitPayload) (context.Context, error) {
return webSocketInit(ctx, initPayload)
},
KeepAlivePingInterval: 10 * time.Second,
})
func webSocketInit(ctx context.Context, initPayload transport.InitPayload) (context.Context, error) {
//Process auth token
ctxNew := context.WithValue(ctx, "token", token)
return ctxNew, nil
}
//client.tsx
useEffect(() => {
const unsubscribe = subscribeToMore({
document: MY_SUBSCRIPTION_GQL,
updateQuery: (prev, { subscriptionData }: any) => {
//...process new data
return Object.assign({}, prev, {
data: newData,
});
},
onError: (e) => {
console.log(e);
},
});
return () => unsubscribe();
}, []);
//apolloclient
const authLink = setContext((_, { headers }) => {
const token = store.getState().user.token;
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
};
});
const wsLink =
typeof window !== 'undefined'
? new GraphQLWsLink(
createClient({
url: 'ws://localhost:8080',
lazy: true,
connectionParams: () => ({
Authorization: store.getState().user.token,
}),
on: {
connected: () => {
console.log('connected');
},
error: (e) => {
console.log(e);
},
},
})
)
: null;
// HTTP Requests
const httpLink = new HttpLink({
uri: `http://localhost:8080/`,
});
const link =
typeof window !== 'undefined' && wsLink != null
? split(
({ query }) => {
const def = getMainDefinition(query);
return def.kind === 'OperationDefinition' && def.operation === 'subscription';
},
wsLink,
authLink.concat(httpLink)
)
: authLink.concat(httpLink);
export const client = new ApolloClient({
link: link,
cache: new InMemoryCache({
addTypename: false,
}),
});

Related

Not getting Apollo Client subscription data

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

SocketIO 4 - won't emit to the room

I have following server code:
const path = require("path");
const http = require("http");
const express = require("express");
const {instrument} = require('#socket.io/admin-ui')
const {jwtDecode, jwtVerify, resignJwt} = require('jwt-js-decode')
const secret =
"xxxxxxx";
const app = express()
const server = http.createServer(app)
const io = require("socket.io")(server, {
cors: {
origin: ["https://admin.socket.io", "http://localhost:3001"],
credentials: true
},
});
let servantID = ''
io.use((socket, next) => {
const header = socket.handshake.headers["authorization"];
jwtVerify(header, secret).then((res) => {
if (res === true)
{
const jwt = jwtDecode(header);
servantID = jwt.payload.iss;
return next()
}
return next(new Error("authentication error"));
});
});
instrument(io, { auth: false });
server.listen(3000, () =>
console.log('connected')
)
io.on('connection', socket => {
socket.on("join", (room, cb) => {
console.log('Joined ' + room);
socket.join(room);
cb(`Joined the best room ${room}`)
});
socket.on('newOrder', function (data) {
socket.to('servant').emit('this', data);
console.log(data);
})
socket.on("thisNew", function (data) {
console.log('this new');
});
socket.on('disconnect', () => {
console.log('user disconnected');
});
})
And client side code:
socket.emit('join', 'servant', message => {
console.log(message)
})
socket.on('this', () => {
console.log('this event')
})
socket.emit('newOrder', 'data')
When I emit like this:
socket.to('servant').emit('this', data);
the client doesn't receive anything, but if I emit without room:
socket.emit('this', data);
the event and data are received on the client side.
What am I doing wrong here?

How to implement an optimistic update when using reduxjs/toolkit

My reducer file is below
const slice = createSlice({
name: "hotels",
initialState: {
list: [],
loading: false,
lastFetch: null,
},
reducers: {
hotelsRequested: (hotels) => {
hotels.loading = true;
},
hotelsRequestFailed: (hotels) => {
hotels.loading = false;
},
hotelsReceived: (hotels, action) => {
hotels.list = action.payload;
hotels.loading = false;
hotels.lastFetch = Date.now();
},
hotelEnabled: (hotels, action) => {
const { slug } = action.payload;
const index = hotels.list.findIndex((hotel) => hotel.slug === slug);
hotels.list[index].active = true;
},
},
});
export const {
hotelsReceived,
hotelsRequestFailed,
hotelsRequested,
hotelEnabled,
} = slice.actions;
export default slice.reducer;
//Action creators
export const loadHotels = () => (dispatch, getState) => {
const { lastFetch } = getState().entities.hotels;
const diffInMinutes = moment().diff(lastFetch, "minutes");
if (diffInMinutes < 10) return;
dispatch(
hotelApiCallBegan({
url: hotelUrl,
onStart: hotelsRequested.type,
onSuccess: hotelsReceived.type,
onError: hotelsRequestFailed.type,
})
);
};
export const enableHotel = (slug) =>
hotelApiCallBegan(
{
url: `${hotelUrl}${slug}/partial-update/`,
method: "put",
data: { active: true },
onSuccess: hotelEnabled.type,
},
console.log(slug)
);
My api request middleware function is as follows
export const hotelsApi = ({ dispatch }) => (next) => async (action) => {
if (action.type !== actions.hotelApiCallBegan.type) return next(action);
const {
onStart,
onSuccess,
onError,
url,
method,
data,
redirect,
} = action.payload;
if (onStart) dispatch({ type: onStart });
next(action);
try {
const response = await axiosInstance.request({
baseURL,
url,
method,
data,
redirect,
});
//General
dispatch(actions.hotelApiCallSuccess(response.data));
//Specific
if (onSuccess) dispatch({ type: onSuccess, payload: response.data });
} catch (error) {
//general error
dispatch(actions.hotelApiCallFailed(error.message));
console.log(error.message);
//Specific error
if (onError) dispatch({ type: onError, payload: error.message });
console.log(error.message);
}
};
Could anyone point me in the right direction of how to add an optimistic update reducer to this code. Currently on hitting enable button on the UI there's a lag of maybe second before the UI is updated. Or maybe the question, is do i create another middleware function to handle optimistic updates? If yes how do i go about that? Thanks

window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory(...).then is not a function

I'm using webchat-es5.js and its throwing error like window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory(...).then is not a function. Need IE support also using es5.js.
window.fetch('https://webchat-mockbot.azurewebsites.net/directline/token', { method: 'POST' })
.then(function (res) {
return res.json();
})
.then(function (json) {
const token = json.token;
window.fetch('https://webchat-mockbot.azurewebsites.net/speechservices/token', { method: 'POST' })
.then(function (res) {
return res.json();
})
.then(function (json) {
const region = json.region;
const authorizationToken = json.token;
window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory({ authorizationToken: authorizationToken , region: region })
.then(function (webSpeechPonyfillFactory) {
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({ token: token }),
webSpeechPonyfillFactory: webSpeechPonyfillFactory
}, document.getElementById('webchat'));
document.querySelector('#webchat > *').focus();
});
});
});
Thanks in advance.
This is the setup that is working for me. I run a server locally to fetch the actual tokens. This way I don't need to include any secrets in the client.
window.fetch( 'http://localhost:3500/directline/token', { method: 'POST' } )
.then( function ( res ) {
return res.json();
} )
.then( function ( res ) {
let token = res.token;
window.fetch( 'http://localhost:3500/speechservices/token', { method: 'POST' } )
.then( function ( res ) {
return res.json();
} )
.then( function ( res ) {
let authorizationToken = res.authorizationToken;
window.WebChat.renderWebChat( {
directLine: window.WebChat.createDirectLine( {
token: token,
webSocket: true
} ),
webSpeechPonyfillFactory: window.WebChat.createCognitiveServicesSpeechServicesPonyfillFactory( {
region: 'westus2',
authorizationToken: authorizationToken
} )
}, document.getElementById( 'webchat' ) );
});
} );
Hope of help!

Apollo Client delays request

I have a very weird issue with Apollo Client.
We are using apollo-client#1.9.3 with react (react-apollo#1.4.16).
In our project, we notice that apollo always wait for 1 to 2 seconds before sending the request.
Below is a screenshot of the situation:
This is how our client config looks like:
const customNetworkInterface = {
query: request =>
fetch('/graphql', {
method: 'POST',
credentials: 'include',
mode: 'cors',
cache: 'default',
headers: {
Accept: '*/*',
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
},
body: JSON.stringify({
...request,
query: print(request.query),
}),
})
.then(resp => resp.json())
.then(({ data, errors }) => {
if (errors) {
const userErrors = errors
.filter(({ code }) => +code >= 400 && +code <= 401)
.map(({ message }) => message)
.join('\n');
const serverErrors = errors
.filter(
({ code }) => !code || (+code < 400 && +code > 401)
)
.map(({ message }) => message)
.join('\n');
if (serverErrors.length > 0) {
error(serverErrors);
if (isProduction) {
window.triggerAlert(
'danger',
'The server encountered an error. Our technical team has been notified.'
);
} else {
window.triggerAlert('danger', serverErrors);
}
} else if (userErrors.length > 0) {
window.triggerAlert('danger', userErrors);
}
}
return { data, errors };
}),
};
const networkInterface = createNetworkInterface({
uri: '/graphql',
opts: {
credentials: 'same-origin',
},
});
networkInterface.useAfter([
{
applyAfterware({ response }, next) {
response
.clone()
.json()
.then(responseJson => {
if (responseJson.errors) {
error(
responseJson.errors
.map(({ message }) => message)
.join('\n')
);
}
next();
});
},
},
]);
export const client = new ApolloClient({
networkInterface: customNetworkInterface,
queryDeduplication: true,
addTypename: true,
});
Then the query code is with react-apollo:
graphql(RaceResultsQuery, {
props: ({ ownProps, data }) => ({
race_results: _.get(data, 'me.my_race_results', []),
}),
}),
This would need a complete, minimal example to provide an answer for sure (delete as much of your code as possible with the issue still happening).
My guess would be that you have a parent component with a very expensive query and it only renders the component with the delayed query after the expensive query returned.

Resources