diff --git a/CHANGELOG.md b/CHANGELOG.md index d5f3c094..3af2c396 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ # Unreleased +- Support `@alias` fragments. + # 3.0.0-rc.9 - Fix client extension enums mixup with `FutureAddedValue`. diff --git a/packages/relay b/packages/relay index 72f4958b..7951c17e 160000 --- a/packages/relay +++ b/packages/relay @@ -1 +1 @@ -Subproject commit 72f4958b940166a3c9ee9ffd21a0e1d232cb1797 +Subproject commit 7951c17e5dc260bd534c0d512db7159964fa5706 diff --git a/packages/rescript-relay/__tests__/Test_aliasedFragments-tests.js b/packages/rescript-relay/__tests__/Test_aliasedFragments-tests.js new file mode 100644 index 00000000..b54e6055 --- /dev/null +++ b/packages/rescript-relay/__tests__/Test_aliasedFragments-tests.js @@ -0,0 +1,27 @@ +require("@testing-library/jest-dom/extend-expect"); +const t = require("@testing-library/react"); +const React = require("react"); +const queryMock = require("./queryMock"); + +const { test_aliasedFragments } = require("./Test_aliasedFragments.bs"); + +describe("Aliased fragments", () => { + test("basic fragments work", async () => { + queryMock.mockQuery({ + name: "TestAliasedFragmentsQuery", + variables: { + skipThing: false, + }, + data: { + loggedInUser: { + id: "user-1", + firstName: "First", + lastName: "Last", + }, + }, + }); + + t.render(test_aliasedFragments()); + await t.screen.findByText("First Last"); + }); +}); diff --git a/packages/rescript-relay/__tests__/Test_aliasedFragments.res b/packages/rescript-relay/__tests__/Test_aliasedFragments.res new file mode 100644 index 00000000..acce023c --- /dev/null +++ b/packages/rescript-relay/__tests__/Test_aliasedFragments.res @@ -0,0 +1,58 @@ +module FragmentFirstName = %relay(` + fragment TestAliasedFragments_userFirstName on User { + firstName + } +`) + +module FragmentLastName = %relay(` + fragment TestAliasedFragments_userLastName on User { + lastName + } +`) + +module Query = %relay(` + query TestAliasedFragmentsQuery($skipThing: Boolean!) { + loggedInUser { + ...TestAliasedFragments_userFirstName @alias + ...TestAliasedFragments_userLastName @alias @skip(if: $skipThing) + } + } +`) + +module Test = { + @react.component + let make = () => { + let query = Query.use(~variables={skipThing: false}) + let firstNameData = FragmentFirstName.use( + query.loggedInUser.testAliasedFragments_userFirstName.fragmentRefs, + ) + let lastNameData = FragmentLastName.useOpt( + query.loggedInUser.testAliasedFragments_userLastName->Belt.Option.map(f => f.fragmentRefs), + ) + +
+ {React.string( + firstNameData.firstName ++ + " " ++ + switch lastNameData { + | None => "-" + | Some({lastName}) => lastName + }, + )} +
+ } +} + +@live +let test_aliasedFragments = () => { + let network = RescriptRelay.Network.makePromiseBased(~fetchFunction=RelayEnv.fetchQuery) + + let environment = RescriptRelay.Environment.make( + ~network, + ~store=RescriptRelay.Store.make(~source=RescriptRelay.RecordSource.make()), + ) + + + + +} diff --git a/packages/rescript-relay/__tests__/__generated__/TestAliasedFragmentsQuery_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestAliasedFragmentsQuery_graphql.res new file mode 100644 index 00000000..e5f86b28 --- /dev/null +++ b/packages/rescript-relay/__tests__/__generated__/TestAliasedFragmentsQuery_graphql.res @@ -0,0 +1,257 @@ +/* @sourceLoc Test_aliasedFragments.res */ +/* @generated */ +%%raw("/* @generated */") +module Types = { + @@warning("-30") + + type rec response_loggedInUser_TestAliasedFragments_userFirstName = { + fragmentRefs: RescriptRelay.fragmentRefs<[ | #TestAliasedFragments_userFirstName]>, + } + and response_loggedInUser_TestAliasedFragments_userLastName = { + fragmentRefs: RescriptRelay.fragmentRefs<[ | #TestAliasedFragments_userLastName]>, + } + and response_loggedInUser = { + @as("TestAliasedFragments_userFirstName") testAliasedFragments_userFirstName: response_loggedInUser_TestAliasedFragments_userFirstName, + @as("TestAliasedFragments_userLastName") testAliasedFragments_userLastName: option, + } + type response = { + loggedInUser: response_loggedInUser, + } + @live + type rawResponse = response + @live + type variables = { + skipThing: bool, + } + @live + type refetchVariables = { + skipThing: option, + } + @live let makeRefetchVariables = ( + ~skipThing=?, + ): refetchVariables => { + skipThing: skipThing + } + +} + + +type queryRef + +module Internal = { + @live + let variablesConverter: Js.Dict.t>> = %raw( + json`{}` + ) + @live + let variablesConverterMap = () + @live + let convertVariables = v => v->RescriptRelay.convertObj( + variablesConverter, + variablesConverterMap, + Js.undefined + ) + @live + type wrapResponseRaw + @live + let wrapResponseConverter: Js.Dict.t>> = %raw( + json`{"__root":{"loggedInUser_TestAliasedFragments_userLastName":{"f":""},"loggedInUser_TestAliasedFragments_userFirstName":{"f":""}}}` + ) + @live + let wrapResponseConverterMap = () + @live + let convertWrapResponse = v => v->RescriptRelay.convertObj( + wrapResponseConverter, + wrapResponseConverterMap, + Js.null + ) + @live + type responseRaw + @live + let responseConverter: Js.Dict.t>> = %raw( + json`{"__root":{"loggedInUser_TestAliasedFragments_userLastName":{"f":""},"loggedInUser_TestAliasedFragments_userFirstName":{"f":""}}}` + ) + @live + let responseConverterMap = () + @live + let convertResponse = v => v->RescriptRelay.convertObj( + responseConverter, + responseConverterMap, + Js.undefined + ) + type wrapRawResponseRaw = wrapResponseRaw + @live + let convertWrapRawResponse = convertWrapResponse + type rawResponseRaw = responseRaw + @live + let convertRawResponse = convertResponse + type rawPreloadToken<'response> = {source: Js.Nullable.t>} + external tokenToRaw: queryRef => rawPreloadToken = "%identity" +} +module Utils = { + @@warning("-33") + open Types +} + +type relayOperationNode +type operationType = RescriptRelay.queryNode + + +let node: operationType = %raw(json` (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "skipThing" + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "TestAliasedFragmentsQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "loggedInUser", + "plural": false, + "selections": [ + { + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "TestAliasedFragments_userFirstName" + }, + "kind": "AliasedFragmentSpread", + "name": "TestAliasedFragments_userFirstName", + "type": "User", + "abstractKey": null + }, + { + "condition": "skipThing", + "kind": "Condition", + "passingValue": false, + "selections": [ + { + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "TestAliasedFragments_userLastName" + }, + "kind": "AliasedFragmentSpread", + "name": "TestAliasedFragments_userLastName", + "type": "User", + "abstractKey": null + } + ] + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "TestAliasedFragmentsQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "loggedInUser", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "firstName", + "storageKey": null + }, + { + "condition": "skipThing", + "kind": "Condition", + "passingValue": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "lastName", + "storageKey": null + } + ] + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "7904bbabe4aaf72255a7e48d9de20bab", + "id": null, + "metadata": {}, + "name": "TestAliasedFragmentsQuery", + "operationKind": "query", + "text": "query TestAliasedFragmentsQuery(\n $skipThing: Boolean!\n) {\n loggedInUser {\n ...TestAliasedFragments_userFirstName\n ...TestAliasedFragments_userLastName @skip(if: $skipThing)\n id\n }\n}\n\nfragment TestAliasedFragments_userFirstName on User {\n firstName\n}\n\nfragment TestAliasedFragments_userLastName on User {\n lastName\n}\n" + } +}; +})() `) + +@live let load: ( + ~environment: RescriptRelay.Environment.t, + ~variables: Types.variables, + ~fetchPolicy: RescriptRelay.fetchPolicy=?, + ~fetchKey: string=?, + ~networkCacheConfig: RescriptRelay.cacheConfig=?, +) => queryRef = ( + ~environment, + ~variables, + ~fetchPolicy=?, + ~fetchKey=?, + ~networkCacheConfig=?, +) => + RescriptRelay.loadQuery( + environment, + node, + variables->Internal.convertVariables, + { + fetchKey, + fetchPolicy, + networkCacheConfig, + }, + ) + +@live +let queryRefToObservable = token => { + let raw = token->Internal.tokenToRaw + raw.source->Js.Nullable.toOption +} + +@live +let queryRefToPromise = token => { + Js.Promise.make((~resolve, ~reject as _) => { + switch token->queryRefToObservable { + | None => resolve(Error()) + | Some(o) => + open RescriptRelay.Observable + let _: subscription = o->subscribe(makeObserver(~complete=() => resolve(Ok()))) + } + }) +} diff --git a/packages/rescript-relay/__tests__/__generated__/TestAliasedFragments_userFirstName_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestAliasedFragments_userFirstName_graphql.res new file mode 100644 index 00000000..5b59184c --- /dev/null +++ b/packages/rescript-relay/__tests__/__generated__/TestAliasedFragments_userFirstName_graphql.res @@ -0,0 +1,60 @@ +/* @sourceLoc Test_aliasedFragments.res */ +/* @generated */ +%%raw("/* @generated */") +module Types = { + @@warning("-30") + + type fragment = { + firstName: string, + } +} + +module Internal = { + @live + type fragmentRaw + @live + let fragmentConverter: Js.Dict.t>> = %raw( + json`{}` + ) + @live + let fragmentConverterMap = () + @live + let convertFragment = v => v->RescriptRelay.convertObj( + fragmentConverter, + fragmentConverterMap, + Js.undefined + ) +} + +type t +type fragmentRef +external getFragmentRef: + RescriptRelay.fragmentRefs<[> | #TestAliasedFragments_userFirstName]> => fragmentRef = "%identity" + +module Utils = { + @@warning("-33") + open Types +} + +type relayOperationNode +type operationType = RescriptRelay.fragmentNode + + +let node: operationType = %raw(json` { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "TestAliasedFragments_userFirstName", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "firstName", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +} `) + diff --git a/packages/rescript-relay/__tests__/__generated__/TestAliasedFragments_userLastName_graphql.res b/packages/rescript-relay/__tests__/__generated__/TestAliasedFragments_userLastName_graphql.res new file mode 100644 index 00000000..8d10772f --- /dev/null +++ b/packages/rescript-relay/__tests__/__generated__/TestAliasedFragments_userLastName_graphql.res @@ -0,0 +1,60 @@ +/* @sourceLoc Test_aliasedFragments.res */ +/* @generated */ +%%raw("/* @generated */") +module Types = { + @@warning("-30") + + type fragment = { + lastName: string, + } +} + +module Internal = { + @live + type fragmentRaw + @live + let fragmentConverter: Js.Dict.t>> = %raw( + json`{}` + ) + @live + let fragmentConverterMap = () + @live + let convertFragment = v => v->RescriptRelay.convertObj( + fragmentConverter, + fragmentConverterMap, + Js.undefined + ) +} + +type t +type fragmentRef +external getFragmentRef: + RescriptRelay.fragmentRefs<[> | #TestAliasedFragments_userLastName]> => fragmentRef = "%identity" + +module Utils = { + @@warning("-33") + open Types +} + +type relayOperationNode +type operationType = RescriptRelay.fragmentNode + + +let node: operationType = %raw(json` { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "TestAliasedFragments_userLastName", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "lastName", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +} `) + diff --git a/packages/rescript-relay/relay.config.js b/packages/rescript-relay/relay.config.js index 1b126ecd..c3d9e205 100644 --- a/packages/rescript-relay/relay.config.js +++ b/packages/rescript-relay/relay.config.js @@ -15,6 +15,9 @@ module.exports = { }, featureFlags: { enable_relay_resolver_transform: true, + enable_fragment_aliases: { + kind: "enabled", + }, }, persistConfig: PERSISTING ? {