Closed moritz157 closed 2 years ago
It seems I finally solved it (some testing still needed though)🚀
The main problem were that I needed to create different mappings for subscriptions when mapping the schema and that the way you have to map subscriptions is different to mapping for example queries or mutations.
Your directive transformer has to look something like this:
function myDirectiveTransformer(schema: GraphQLSchema): GraphQLSchema {
const typeDirectiveArgumentMaps: Record<string, Record<string, string>> = {};
return mapSchema(schema, {
[MapperKind.SUBSCRIPTION]: type => {
const myDirective = getDirective(schema, type, 'myDirective')?.[0]
if (myDirective) {
typeDirectiveArgumentMaps[type.name] = myDirective
}
return undefined
},
[MapperKind.SUBSCRIPTION_ROOT_FIELD]: (fieldConfig, _fieldName, typeName) => {
const myDirective: Record<string, string> = getDirective(schema, fieldConfig, 'myDirective')?.[0] ?? typeDirectiveArgumentMaps[typeName]
if (myDirective) {
const { myDirectiveParameter } = myDirective
const { resolve = defaultFieldResolver } = fieldConfig;
const defaultFieldSubscription = fieldConfig.subscribe;
fieldConfig.subscribe = async (rootValue: any, args: { [argName: string]: any }, context: { [argName: string]: unknown }, info: GraphQLResolveInfo): Promise<any> => {
try {
// Manipulate context here
context.someContextVariable = 'hello there';
// If you want to throw an error (for example because authentication failed), just throw it here (or in any functions called here)
} catch(error) {
context.error = error;
const emptyIterator = {
done: false,
next: (): Promise<IteratorResult<any>> => {
if(emptyIterator.done) return Promise.resolve({ value: undefined, done: true }) ;
else {
emptyIterator.done = true;
return Promise.resolve({ value: undefined, done: false });
}},
return: () => Promise.resolve({ value: undefined, done: true }),
throw: (err) => Promise.reject(err),
[$$asyncIterator] (): any { return emptyIterator; }
};
return emptyIterator;
}
return defaultFieldSubscription(rootValue, args, context, info);
}
fieldConfig.resolve = async (source, args: { [argsName: string]: any }, context: { error?: unknown }, info: GraphQLResolveInfo): Promise<unknown> => {
if(context.error) throw context.error;
return resolve(source, args, context, info);
}
return fieldConfig
}
},
})
}
Perhaps we should add documentation for it somewhere because I couldn't find any about this, which was extremely frustating. I am not really sure where though, especially since this repo seems to be more or less abandonded.
I want to use a directive to authorize subscription requests since I already use it to authorize my regular queries and mutations. The directive is apparently getting called (before the subscription) but when I add session information to the context, I cannot access it using the context provided when using
withFilter
. It appears that the context accessible in the directive transformer and the context inwithFilter
are different from one another.Why is that or am I doing something wrong? Is there an easy way around this? Is it even possible to properly use directives with subscriptions?
My code is basically: