-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Can't override onConnect
for subscriptions
#75
Comments
@Hugome could you help this issue? |
I'm wondering if the changes I've recommended in #73 would alleviate the need to override the |
@shawnmcknight I believe so. There are a few main stages I would like to consider in a normal subscription/WebSocket operation:
I'm currently working with a fork of Thanks for everyone's attention to this. My efforts have been hampered by a lack of understanding in regard to how Moleculer ingresses operations and creates the initial context in general. |
Ok. After some more time looking into this, I've found that by simply adding a way to hook into both this.apolloServer = new ApolloServer({
schema,
..._.defaultsDeep({}, mixinOptions.serverOptions, {
context: async (...args) => {
const { req, connection } = args[0]
let customContext;
if (this.context) customContext = await this.context(...args);
return req
? {
ctx: req.$ctx,
service: req.$service,
params: req.$params,
dataLoaders: new Map(), // create an empty map to load DataLoader instances into
}
: {
service: connection.context.$service,
...customContext,
// ctx: connection.context.$ctx
};
},
subscriptions: {
onConnect: async (connectionParams, socket) => {
let customParams;
if (this.onConnect) customParams = await this.onConnect(connectionParams, socket);
return ({
...connectionParams,
...customParams,
$service: this,
});
},
},
}),
}); Both are important. |
This seems to satisfy our needs: createAsyncIteratorResolver(actionName, tags = [], filter) {
return {
subscribe: filter
? withFilter(
() => this.pubsub.asyncIterator(tags),
async (payload, params, graphqlCtx) => {
// Use the context now provided in the graphql context. Make sure to not pass
// Moleculer context to opts
const { ctx, ...opts } = graphqlCtx;
return payload !== undefined
? ctx.call(filter, { ...params, payload }, opts)
: false;
}
)
: () => this.pubsub.asyncIterator(tags),
resolve: async (payload, params, graphqlCtx) => {
// same as above.
const { ctx, ...opts } = graphqlCtx;
return ctx.call(actionName, { ...params, payload }, opts);
},
};
},
//......
this.apolloServer = new ApolloServer({
schema,
..._.defaultsDeep({}, mixinOptions.serverOptions, {
context: async (...args) => {
const { req, connection } = args[0]
// Allow custom contexts to be passed along
let customContext;
if (this.context) customContext = await this.context(...args);
// Consitently pass context regardless of source
return req
? {
ctx: req.$ctx,
service: req.$service,
params: req.$params,
dataLoaders: new Map(), // create an empty map to load DataLoader instances into
}
: {
service: connection.context.$service,
...customContext,
ctx: connection.context.$ctx,
};
},
subscriptions: {
onConnect: async (connectionParams, socket) => {
// create a context right away.
const $ctx = this.broker.ContextFactory.create(
this.broker,
null,
{},
{}
);
socket.$ctx = $ctx;
// Allow custom contexts to be passed along
let customParams;
if (this.onConnect)
customParams = await this.onConnect(
connectionParams,
socket
);
return {
...connectionParams,
...customParams,
$service: this,
$ctx,
};
},
},
}),
}); |
In the above code, my biggest concern is how we are managing Moleculer context. Is it correct to create a context |
Joining @shawnmcknight, you will have a Moleculer context & more customization possibility with #73 and the new |
@shawnmcknight @Hugome I put this into Discord and then realized I should really add to the conversation here... After taking a break to work on a different part of our app, I'm back on trying to get this working (which is now critical for us). #73 was merged, but, unless I'm misunderstanding, it misses the per-operation case mentioned above. I'm working on rewriting my example code to hook the new Our code currently checks on every operation to see if the token we have is expired. We do this by hooking into Apollo's context thus allowing us to always check or update credentials as required. I think I may just be able to merge in my own implementation by copying molleculer-apollo's code and adding my own and overwriting the Apollo context function. But this is brittle. Is this the best way? Using the new code, I'm seeing operations over that connection return a REQUEST_SKIPPED most of the time. Sometimes it works, but rarely. Does the context keep track of a time that is shared between operations? |
Ok... I'm pretty sure this isn't going to work. I think the context is maintaining an hrtime from when the |
Ok. Two issues remain to be fixed:
I'd like to work on a solution for this. But first I need to come up with an approach for handling the context. My thoughts are expressed above #75 (comment) |
@icebob Following on #75 (comment) and #75 (comment) I am trying to figure out what kind of context handing is preferable here. It seems, for tracking purposes, we would want to make a context at the WebSocket connection time, and then a child context for each operation over that connection. But my main problem right now is that that will exceed the What would be your desired approach from a design standpoint? (I'm still learning much about Moleculer's architecture and design choices, so I don't feel safe trying to implement anything yet) |
Yeah, maybe creating a traced/tracked context is not a good option, if you never close it. Because you will lose the nested tracing for these calls (because the main parent span is never finished). Another problem is that if you use Sorry, but in the last months, I'm working totally different things, so I don't feel I understand the issue totally and I'm not using this module nowadays. Could you create a repo which contains only minimal things to demonstrate the problem and comments about what would be the desired "output"? Maybe it helps me to see the problem comprehensively. |
I already made a code sandbox showing the timeout issue above (Here's the link again). But perhaps that shows an issue with the latest MR more than what I'd like to see happen... It does illustrate the desired operation. A single WebSocket connection, with support for multiple operations over that connection. Normally each operation/call/message would be equivalent to a single REST call. So it would make sense to track each one as such. But it would also be nice if we could somehow still map/track each call to it's parent WebSocket connection. |
@icebob @shawnmcknight @Hugome
Part two feels hacky. It still feels like we should be creating a new context for every operation. Furthermore, it feels like we should have better traces. With the code above, a single WebSocket, then operation, will result in
Where this would seem preferable:
I plan to put what is in the code sandbox into an MR tomorrow morning... any feedback is appreciated. I'd really like to get the context and tracing stuff setup properly though. |
@shawnmcknight @icebob |
In order to properly do auth for subscriptions (the directive approach oddly doesn't work for subs), and configure the context in general, I need to be able to override it. This can be partially done by overriding it in the server settings. Unfortunately, when you add the need to reference
this
, it falls apart. Also, you need to replicate the existing method implementation, which is brittle and not likely to survive core library updates. Any advice on how to progress here? Seems like #73 might be related. Hopefully the fixes there will allow us to block the subscription at initialization since I've also noticed we can't seem to do much other than filter it lazily.The text was updated successfully, but these errors were encountered: