Skip to content

Commit

Permalink
Add context to relay resolvers (#4704)
Browse files Browse the repository at this point in the history
Summary:
This is a draft PR to implement an approach mentioned in #4000.

I've currently put the `resolverContext` on the `Store` instead of the `Environment`.  It seems that the `Store` might be a better place than the `Environment`?

If defined on the environment the context would need to go from the environment into the store down to the reader? Since a store could be used by different environments if the resolverContext would be on the environment that might cause more issues than it being on the store?

The following is an example of how this would be used.

One would provide the context as follows.

```ts
const store = new LiveResolverStore(source, {
    resolverContext: {
      counter: createObservableCounter(),
      world: "World!"
    },
  });
  ```

Then the resolvers could use the context similar to `readFragment`

```ts
/**
 * RelayResolver Query.hello_context: String
 *
 * Say `Hello, ${world}!`
 */
import {resolverContext} from '../../ResolverFragments';

function hello_context(): string {
  const world = resolverContext<{world: string}>().world;
  return `Hello, ${world}!`;
}
```

```ts
/**
 * RelayResolver Query.hello_world_with_context: String
 * live
 *
 * Say `Hello ${world}!`
 */
function hello_world_with_context(): LiveState<string> {
  const dependency = resolverContext<{greeting: string}>();

  return {
    read() {
      return `Hello ${dependency.greeting}!`;
    },

    subscribe(callback) {
      return () => {
        // no-op
      };
    },
  };
}
```

Pull Request resolved: #4704

Reviewed By: lynnshaoyu

Differential Revision: D61561442

Pulled By: captbaritone

fbshipit-source-id: d81608a58aff6067db77fdef2c03036c0a82dbeb
  • Loading branch information
Markionium authored and facebook-github-bot committed Sep 4, 2024
1 parent f07f56e commit 2973a0f
Show file tree
Hide file tree
Showing 212 changed files with 2,269 additions and 240 deletions.
98 changes: 98 additions & 0 deletions compiler/crates/relay-compiler/relay-compiler-config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1488,6 +1488,55 @@
"default": false,
"type": "boolean"
},
"resolverContextType": {
"description": "Indicates the type to import and use as the context for live resolvers.",
"default": null,
"anyOf": [
{
"anyOf": [
{
"description": "Specifies how Relay can import the Resolver context type from a path",
"type": "object",
"required": [
"name",
"path"
],
"properties": {
"name": {
"description": "The name under which the type is exported from the module",
"type": "string"
},
"path": {
"description": "The path to the module relative to the project root",
"type": "string"
}
}
},
{
"description": "Specifies how Relay can import the Resolver context type from a named package",
"type": "object",
"required": [
"name",
"package"
],
"properties": {
"name": {
"description": "The name under which the type is exported from the package",
"type": "string"
},
"package": {
"description": "The name of the package",
"type": "string"
}
}
}
]
},
{
"type": "null"
}
]
},
"resolversSchemaModule": {
"description": "Configuration for resolvers_schema_module generation",
"default": null,
Expand Down Expand Up @@ -4521,6 +4570,55 @@
"default": false,
"type": "boolean"
},
"resolverContextType": {
"description": "Indicates the type to import and use as the context for live resolvers.",
"default": null,
"anyOf": [
{
"anyOf": [
{
"description": "Specifies how Relay can import the Resolver context type from a path",
"type": "object",
"required": [
"name",
"path"
],
"properties": {
"name": {
"description": "The name under which the type is exported from the module",
"type": "string"
},
"path": {
"description": "The path to the module relative to the project root",
"type": "string"
}
}
},
{
"description": "Specifies how Relay can import the Resolver context type from a named package",
"type": "object",
"required": [
"name",
"package"
],
"properties": {
"name": {
"description": "The name under which the type is exported from the package",
"type": "string"
},
"package": {
"description": "The name of the package",
"type": "string"
}
}
}
]
},
{
"type": "null"
}
]
},
"resolversSchemaModule": {
"description": "Configuration for resolvers_schema_module generation",
"default": null,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
==================================== INPUT ====================================
//- User_foo.js
/**
* @RelayResolver User.foo: RelayResolverValue!
* @rootFragment UserFooFragment
*/
graphql`fragment UserFooFragment on User {
bar
}`

//- User_bar.js
/**
* @RelayResolver User.bar: RelayResolverValue!
*/

//- relay.config.json
{
"language": "javascript",
"jsModuleFormat": "haste",
"schema": "schema.graphql",
"featureFlags": {
"enable_relay_resolver_transform": true,
"enable_resolver_normalization_ast": true,
"allow_resolver_non_nullable_return_type": { "kind": "enabled" }
},
"resolverContextType": { "name": "TestResolverContextType", "path": "./test-interface" }
}

//- schema.graphql
type Query { me: User }
type User { name: String }
==================================== OUTPUT ===================================
//- __generated__/UserFooFragment.graphql.js
/**
* <auto-generated> SignedSource<<a41941282ea98ca7467742ce74966b2a>>
* @lightSyntaxTransform
* @nogrep
*/

/* eslint-disable */

'use strict';

var node = {
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "UserFooFragment",
"selections": [
{
"kind": "ClientExtension",
"selections": [
{
"alias": null,
"args": null,
"fragment": null,
"kind": "RelayResolver",
"name": "bar",
"resolverModule": require('User_bar').bar,
"path": "bar"
}
]
}
],
"type": "User",
"abstractKey": null
};

node.hash = "285ee53d00b8def775c9e1ed756743bf";

module.exports = node;
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//- User_foo.js
/**
* @RelayResolver User.foo: RelayResolverValue!
* @rootFragment UserFooFragment
*/
graphql`fragment UserFooFragment on User {
bar
}`

//- User_bar.js
/**
* @RelayResolver User.bar: RelayResolverValue!
*/

//- relay.config.json
{
"language": "javascript",
"jsModuleFormat": "haste",
"schema": "schema.graphql",
"featureFlags": {
"enable_relay_resolver_transform": true,
"enable_resolver_normalization_ast": true,
"allow_resolver_non_nullable_return_type": { "kind": "enabled" }
},
"resolverContextType": { "name": "TestResolverContextType", "path": "./test-interface" }
}

//- schema.graphql
type Query { me: User }
type User { name: String }
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
==================================== INPUT ====================================
//- User_foo.js
/**
* @RelayResolver User.foo: RelayResolverValue!
* @rootFragment UserFooFragment
*/
graphql`fragment UserFooFragment on User {
bar
}`

//- User_bar.js
/**
* @RelayResolver User.bar: RelayResolverValue!
*/

//- relay.config.json
{
"language": "flow",
"jsModuleFormat": "haste",
"schema": "schema.graphql",
"featureFlags": {
"enable_relay_resolver_transform": true,
"enable_resolver_normalization_ast": true,
"allow_resolver_non_nullable_return_type": { "kind": "enabled" }
},
"resolverContextType": { "name": "TestResolverContextType", "package": "@test/package" }
}

//- schema.graphql
type Query { me: User }
type User { name: String }
==================================== OUTPUT ===================================
//- __generated__/UserFooFragment.graphql.js
/**
* <auto-generated> SignedSource<<109c452d9b0ac94c8474a71548d2bf85>>
* @flow
* @lightSyntaxTransform
* @nogrep
*/

/* eslint-disable */

'use strict';

/*::
import type { Fragment, ReaderFragment } from 'relay-runtime';
import type { FragmentType } from "relay-runtime";
import {bar as userBarResolverType} from "User_bar";
import type { TestResolverContextType } from "@test/package";
// Type assertion validating that `userBarResolverType` resolver is correctly implemented.
// A type error here indicates that the type signature of the resolver module is incorrect.
(userBarResolverType: (
args: void,
context: TestResolverContextType,
) => $NonMaybeType<mixed>);
declare export opaque type UserFooFragment$fragmentType: FragmentType;
export type UserFooFragment$data = {|
+bar: $NonMaybeType<ReturnType<typeof userBarResolverType>>,
+$fragmentType: UserFooFragment$fragmentType,
|};
export type UserFooFragment$key = {
+$data?: UserFooFragment$data,
+$fragmentSpreads: UserFooFragment$fragmentType,
...
};
*/

var node/*: ReaderFragment*/ = {
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "UserFooFragment",
"selections": [
{
"kind": "ClientExtension",
"selections": [
{
"alias": null,
"args": null,
"fragment": null,
"kind": "RelayResolver",
"name": "bar",
"resolverModule": require('User_bar').bar,
"path": "bar"
}
]
}
],
"type": "User",
"abstractKey": null
};

(node/*: any*/).hash = "285ee53d00b8def775c9e1ed756743bf";

module.exports = ((node/*: any*/)/*: Fragment<
UserFooFragment$fragmentType,
UserFooFragment$data,
>*/);
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//- User_foo.js
/**
* @RelayResolver User.foo: RelayResolverValue!
* @rootFragment UserFooFragment
*/
graphql`fragment UserFooFragment on User {
bar
}`

//- User_bar.js
/**
* @RelayResolver User.bar: RelayResolverValue!
*/

//- relay.config.json
{
"language": "flow",
"jsModuleFormat": "haste",
"schema": "schema.graphql",
"featureFlags": {
"enable_relay_resolver_transform": true,
"enable_resolver_normalization_ast": true,
"allow_resolver_non_nullable_return_type": { "kind": "enabled" }
},
"resolverContextType": { "name": "TestResolverContextType", "package": "@test/package" }
}

//- schema.graphql
type Query { me: User }
type User { name: String }
Loading

0 comments on commit 2973a0f

Please sign in to comment.