Skip to content
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

Fix #894, add perEventContextResolver for subscribe #2485

Conversation

tgriesser
Copy link
Contributor

@tgriesser tgriesser commented Mar 11, 2020

Fixes #894

Adds contextValueExecution perEventContextResolver, a new option for subscribe which allows you to derive a new context value for each independent execution of a subscription.

It takes the original context value as an argument, so you can derive the new context from the original.

@IvanGoncharov IvanGoncharov added the PR: feature 🚀 requires increase of "minor" version number label Mar 12, 2020
@IvanGoncharov
Copy link
Member

@tgriesser I like this solution and I think it is generic enough to be added into lib.
However, I think contextValueExecution is a confusing name but I can't suggest anything better 🤷‍♂
It's the only blocking issue I have.

Also please update JSDoc comments for relevant functions.

@tgriesser
Copy link
Contributor Author

Nice! The idea with the name was to take contextValue, which is the name for the original context, combined with the execute, called in mapSourceToResponse.

Maybe one of:

  • executeContext
  • executeContextValue
  • contextValueExecute
  • contextValueForExecute

Also please update JSDoc comments for relevant functions.

Sounds good, will do once we land on the name.

@IvanGoncharov
Copy link
Member

combined with the execute, called in mapSourceToResponse.

@tgriesser The fact that we call execute under the hood is an implementation detail.
Also since it's a function it should be reflected in the name.

Spec describes "stream of events" so I think it should be something like perEventContextResolver.
http://spec.graphql.org/June2018/#sec-Subscription

@tgriesser
Copy link
Contributor Author

The fact that we call execute under the hood is an implementation detail.
Also since it's a function it should be reflected in the name.

Ah right, that makes sense.

Spec describes "stream of events" so I think it should be something like perEventContextResolver.

Sounds great.

@tgriesser tgriesser changed the title Fix #894, add contextValueExecution for subscribe Fix #894, add perEventContextResolver for subscribe Mar 16, 2020
@tgriesser
Copy link
Contributor Author

@IvanGoncharov docs should be updated, let me know if you see anything that needs correction.

…ription

* master:
  TS: remove TS-specific TSource argument for resolveFieldValueOr… (graphql#2491)
  TS: remove TS-specific TData from ExecutionResult (graphql#2490)
  TS(definition): remove TS-specific TArgs (graphql#2488)
@IvanGoncharov IvanGoncharov added this to the v15.1.0 milestone Mar 25, 2020
@IvanGoncharov
Copy link
Member

@tgriesser Thanks for the update, now it looks perfect 👍
But I don't want to increase scope of 15.0.0 so will merge after release.

…ription

* master:
  v15.0.0
  Update deps (graphql#2502)
  Update Flow (graphql#2500)
  Update prettier (graphql#2499)
  Move custom ESLint rules into internal package (graphql#2498)
@IvanGoncharov IvanGoncharov modified the milestones: v15.1.0, 15.2.0 Jun 20, 2020
@andreialecu
Copy link

andreialecu commented Jul 18, 2020

Just ran into the issue at #894

Seems that the milestone was missed. Anything holding this back?

@97amarnathk
Copy link

97amarnathk commented Nov 11, 2020

@IvanGoncharov @tgriesser I have a similar use case while implementing graphql subscriptions with dataloaders. Any particular reason why this isn't merged yet?

…ription

* master: (211 commits)
  Update deps (graphql#2844)
  resources: use named groups in RegExp (graphql#2840)
  TS: exclude integration tests from root tsconfig.json (graphql#2838)
  Flow: remove support for measuring Flow coverage (graphql#2837)
  CI: test on node 15 (graphql#2836)
  Update deps (graphql#2835)
  build: add support for experimental releases (graphql#2831)
  15.4.0
  Update deps (graphql#2827)
  Update deps (graphql#2825)
  integrationTests: add Flow test (graphql#2819)
  fix: ensure variance of types matches how values are used (graphql#2786)
  Cleanup '__fixtures__' leftovers (graphql#2818)
  Convert fixtures to be JS files (graphql#2816)
  Update deps (graphql#2815)
  benchmark: extract benchmarks into separate folder and run them on NPM package
  Update deps (graphql#2810)
  Update outdated documentation (graphql#2806)
  Make print() break arguments over multiple lines (graphql#2797)
  Added check for specific symbols in polyfills/symbols (graphql#2804)
  ...
@benjie
Copy link
Member

benjie commented Nov 13, 2020

Is there a way to "release" the context? Say, for example, that the context were to contain an authenticated client connected to a datastore.

E.g. for normal GraphQL execution you can do:

function getContext(...) {
  const client = ...;
  return {
    contextValue: { client },
    release: () => client.release(),
  };
}

const { contextValue, release } = getContext(...);
try {
  return await graphql({schema, source, contextValue})
} finally {
  release();
}

@tgriesser
Copy link
Contributor Author

@benjie that's a really good point... the current change wouldn't support that case well.

In thinking about it more, and looking through the subscribe / createSourceEventStream code, I feel a better option is to add a perEventExecute, so you can tap into / modify anything you'd need about the execution. That way you can do something like:

const iterator = await subscribe({
  schema,
  document,
  variableValues,
  contextValue: new DataContext(),
  operationName: obj.request.name,
  perEventExecute: async (execArgs) => {
    const { contextValue, release } = getContext(...);
    try {
      return await execute({...execArgs, contextValue})
    } finally {
      release();
    }
  }
})

@IvanGoncharov thoughts on that? Something like:

export type SubscriptionArgs = {|
  schema: GraphQLSchema,
  document: DocumentNode,
  rootValue?: mixed,
  contextValue?: mixed,
  variableValues?: ?{ +[variable: string]: mixed, ... },
  operationName?: ?string,
  fieldResolver?: ?GraphQLFieldResolver<any, any>,
  subscribeFieldResolver?: ?GraphQLFieldResolver<any, any>,
+ perEventExecute?: (args: ExecutionArgs) => PromiseOrValue<ExecutionResult>
|};
function subscribeImpl(
  args: SubscriptionArgs,
): Promise<AsyncGenerator<ExecutionResult, void, void> | ExecutionResult> {
  const {
    schema,
    document,
    rootValue,
    contextValue,
    variableValues,
    operationName,
    fieldResolver,
    subscribeFieldResolver,
+   perEventExecute = execute
  } = args;
  const mapSourceToResponse = (payload) =>
-   execute({
+   perEventExecute({
      schema,

@97amarnathk
Copy link

97amarnathk commented Jun 4, 2021

@IvanGoncharov Are there any plans to merge this ? If not, then what is the best way to generate a context for each execution currently?

@97amarnathk
Copy link

PR which solves this in another way:
#3071

Copy link

@slumericanBx slumericanBx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

send it

yaacovCR added a commit that referenced this pull request Oct 1, 2024
to safely export buildExecutionContext() as validateExecutionArgs()

motivation: this will allow us to:

1. export `executeOperation()` and `executeSubscription()` for those who would like to use directly.
2. add a `perEventExecutor` option to `ExecutionArgs` that allows one to pass a custom context for execution of each execution event, with the opportunity to clean up the context on return, a la #2485 and #3071, addressing #894, which would not require re-coercion of variables.

The signature of the `perEventExecutor` option would be:

```ts
type SubscriptionEventExecutor = ( validatedExecutionArgs: ValidatedExecutionArgs): PromiseOrValue<ExecutionResult>
```

rather than:

```ts
type SubscriptionEventExecutor = ( executionArgs: ExecutionArgs): PromiseOrValue<ExecutionResult>
```

This might be a first step to integrating `subscribe()` completely into `execute()` (see: #3644) but is also a reasonable stopping place.
yaacovCR added a commit that referenced this pull request Oct 6, 2024
by exporting executeSubscriptionEvent()

and adding option for to provide a custom fn

addresses #894

cf. #2485 , #3071
@yaacovCR
Copy link
Contributor

yaacovCR commented Nov 5, 2024

@yaacovCR yaacovCR closed this Nov 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PR: feature 🚀 requires increase of "minor" version number
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Context lifecycle in subscriptions
7 participants