Skip to content

Commit

Permalink
create transformRelayQueryPayload()
Browse files Browse the repository at this point in the history
Summary: A lot of our tests have to write data into the store before testing some other logic. However, the payloads must be manually constructed using `generateRQLFieldAlias()`. It's not a big deal, but it's annoying.

So this PR adds a new module `transformRelayQueryPayload(query: RelayQuery.Node, payload: Payload): Payload` - it accepts payloads where the keys correspond to field names/aliases (which cannot be written into the store as-is), and returns an equivalent payload where keys are renamed to the serialization key of the corresponding field (which *can* be written to the store). See the unit test for this module for an example, and how it (slightly) simplifies tests that write to the store.

Closes #341

Reviewed By: @yungsters

Differential Revision: D2457487
  • Loading branch information
josephsavona authored and facebook-github-bot-0 committed Sep 22, 2015
1 parent 03607d4 commit 98035f6
Show file tree
Hide file tree
Showing 8 changed files with 556 additions and 218 deletions.
27 changes: 14 additions & 13 deletions src/store/__tests__/RelayStoreData_cacheManager-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ var RelayConnectionInterface = require('RelayConnectionInterface');
var RelayMockCacheManager = require('RelayMockCacheManager');
var RelayMutationType = require('RelayMutationType');
var RelayStoreData = require('RelayStoreData');
var generateRQLFieldAlias = require('generateRQLFieldAlias');

var transformRelayQueryPayload = require('transformRelayQueryPayload');

describe('RelayStoreData', function() {
var cacheManager;
Expand Down Expand Up @@ -274,10 +275,10 @@ describe('RelayStoreData', function() {
}
}
`);
var response = {
var response = transformRelayQueryPayload(query, {
node: {
id: '123',
[generateRQLFieldAlias('friends.first(2)')]: {
friends: {
edges: [
{
node: {
Expand All @@ -298,7 +299,7 @@ describe('RelayStoreData', function() {
},
},
},
};
});
storeData.handleQueryPayload(query, response);

expect(cacheManager).toContainCalledMethods({
Expand Down Expand Up @@ -362,18 +363,18 @@ describe('RelayStoreData', function() {
}
}
`);
var response = {
var response = transformRelayQueryPayload(query, {
node: {
id: '123',
[generateRQLFieldAlias('friends.first(2)')]: {
friends: {
edges: [],
[PAGE_INFO]: {
[HAS_PREV_PAGE]: false,
[HAS_NEXT_PAGE]: true,
},
},
},
};
});
storeData.handleQueryPayload(query, response);

expect(cacheManager).toContainCalledMethods({
Expand Down Expand Up @@ -460,10 +461,10 @@ describe('RelayStoreData', function() {
}
}
`);
var response = {
var response = transformRelayQueryPayload(query, {
node: {
id: '123',
[generateRQLFieldAlias('comments.first(1)')]: {
comments: {
count: 2,
edges: [
{
Expand All @@ -479,7 +480,7 @@ describe('RelayStoreData', function() {
},
},
}
};
});
storeData.handleQueryPayload(query, response);

var configs = [{
Expand Down Expand Up @@ -581,10 +582,10 @@ describe('RelayStoreData', function() {
}
}
`);
var response = {
var response = transformRelayQueryPayload(query, {
node: {
id: '123',
[generateRQLFieldAlias('comments.first(1)')]: {
comments: {
count: 2,
edges: [
{
Expand All @@ -600,7 +601,7 @@ describe('RelayStoreData', function() {
},
},
}
};
});
storeData.handleQueryPayload(query, response);

var configs = [{
Expand Down
4 changes: 4 additions & 0 deletions src/tools/__mocks__/RelayTestUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -480,8 +480,12 @@ var RelayTestUtils = {
var RelayChangeTracker = require('RelayChangeTracker');
var RelayQueryTracker = require('RelayQueryTracker');
var RelayQueryWriter = require('RelayQueryWriter');
var transformRelayQueryPayload = require('transformRelayQueryPayload');
var writeRelayQueryPayload = require('writeRelayQueryPayload');

// rewrite any plain name/alias property names into storage keys
payload = transformRelayQueryPayload(query, payload);

tracker = tracker || new RelayQueryTracker();
options = options || {};
var changeTracker = new RelayChangeTracker();
Expand Down
149 changes: 149 additions & 0 deletions src/tools/__mocks__/transformRelayQueryPayload.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/**
* Copyright 2013-2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule transformRelayQueryPayload
* @flow
* @typechecks
*/

'use strict';

var RelayQuery = require('RelayQuery');
var RelayQueryVisitor = require('RelayQueryVisitor');

var invariant = require('invariant');
var mapObject = require('mapObject');

type Payload = mixed;
type PayloadState = {
client: Payload,
server: Payload,
};

/**
* Transforms "client" payloads with property keys that match the "application"
* names (i.e. property names are schema names or aliases) into "server"
* payloads that match what the server would return for the given query (i.e.
* property names are serialization keys instead).
*/
function transformRelayQueryPayload(
root: RelayQuery.Root,
clientData: Payload
): Payload {
// Handle both FB & OSS formats for root payloads on plural calls: FB
// returns objects with array values, OSS returns arrays.
if (clientData == null) {
return clientData;
} else if (Array.isArray(clientData)) {
return clientData.map(item => transform(root, item));
} else {
invariant(
typeof clientData === 'object',
'transformClientPayload(): Expected the root payload for query `%s` ' +
'to be an array or object, got `%s`.',
root.getName(),
clientData
);
return mapObject(clientData, item => {
if (Array.isArray(item)) {
return item.map(innerItem => transform(root, innerItem));
}
return transform(root, item);
});
}
}

function transform(
root: RelayQuery.Root,
clientData: Payload
): Payload {
if (clientData == null) {
return clientData;
}
var transform = new RelayPayloadTransformer();
var serverData = {};
transform.visit(root, {
client: clientData,
server: serverData,
});
return serverData;
}

class RelayPayloadTransformer extends RelayQueryVisitor<PayloadState> {
visitField(
node: RelayQuery.Field,
state: PayloadState
): ?RelayQuery.Node {
var {client, server} = state;
// `client` represents the *parent* node value and should not be null
// due to checks before traversing child values.
invariant(
typeof client === 'object' && client !== null,
'RelayPayloadTransformer: Expected a client value for field `%s`.',
node.getApplicationName()
);
invariant(
typeof server === 'object' && server !== null,
'RelayPayloadTransformer: Expected a server value for field `%s`.',
node.getApplicationName()
);
var applicationName = node.getApplicationName();
var serializationKey = node.getSerializationKey();
var clientData = client[applicationName];
var serverData = server[serializationKey];

if (node.isScalar() || clientData == null) {
server[serializationKey] = clientData;
} else if (Array.isArray(clientData)) {
invariant(
serverData == null || Array.isArray(serverData),
'RelayPayloadTransformer: Got conflicting values for field `%s`: ' +
'expected values to be arrays.',
applicationName
);
if (serverData == null) {
server[serializationKey] = serverData = [];
}
clientData.forEach((clientItem, index) => {
if (clientItem == null) {
serverData[index] = clientItem;
return;
}
var serverItem = serverData && serverData[index];
if (serverItem == null) {
serverData[index] = serverItem = {};
}
this.traverse(node, {
client: clientItem,
server: serverItem,
});
});
} else {
invariant(
typeof clientData === 'object' && clientData !== null,
'RelayPayloadTransformer: Expected an object value for field `%s`.',
applicationName
);
invariant(
serverData == null || typeof serverData === 'object',
'RelayPayloadTransformer: Got conflicting values for field `%s`: ' +
'expected values to be objects.',
applicationName
);
if (serverData == null) {
server[serializationKey] = serverData = {};
}
this.traverse(node, {
client: clientData,
server: serverData,
});
}
}
}

module.exports = transformRelayQueryPayload;
Loading

0 comments on commit 98035f6

Please sign in to comment.