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

#2606: Allow 'dataPath' to be specified on the GraphQL replication pull object to specify where to fetch result documents from #3195

Merged
merged 5 commits into from
Jun 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

### X.X.X (coming soon)

Features:
- Added `dataPath` property to GraphQL replication pull options to allow the document JSON lookup path to configured instead of assuming the document data is always the first child of the response [#2606](https://github.com/pubkey/rxdb/issues/2606)
Copy link
Owner

Choose a reason for hiding this comment

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

You should add this to the top "comming soon" section, not an already released version.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yep, my master was out of date. Just merged origin/master and resolved the conflict to place this in the correct version.


Types:
- `getLocal()` can return `undefined`. Thanks [@chrisdrackett](https://github.com/chrisdrackett)

Expand Down Expand Up @@ -72,7 +75,7 @@ Other:

### 9.13.0 (10 February 2021)

Features:
Features:

- Added `RxCollection().bulkRemove()` [#2845](https://github.com/pubkey/rxdb/pull/2845) Thanks [@qinyang912](https://github.com/qinyang912)

Expand Down Expand Up @@ -143,7 +146,7 @@ Features:
- Added [RxDocument.atomicPatch()](https://rxdb.info/rx-document.html#atomicpatch)

Bugfixes:
- (types) Returned values of `syncGraphQL()` did not type-match with `RxGraphQLReplicationState`
- (types) Returned values of `syncGraphQL()` did not type-match with `RxGraphQLReplicationState`
- `RxDocument.atomicUpdate()` now does a retry on 409 write conflicts

Other:
Expand All @@ -170,7 +173,7 @@ Features:
Other:
- Refactored GraphQL replication to run faster [#2524](https://github.com/pubkey/rxdb/pull/2524/) Thanks [@corinv](https://github.com/corinv)

### 9.6.0 (7 September 2020)
### 9.6.0 (7 September 2020)

Features:
- Add `RxReplicationState.setHeaders()` [#2399](https://github.com/pubkey/rxdb/pull/2399/) Thanks [@DDoerner](https://github.com/DDoerner)
Expand Down Expand Up @@ -696,7 +699,7 @@ Other:

## 6.0.0 (September 19, 2017) BREAKING

Breaking:
Breaking:
- Filenames are now kebab-case
- `pouchdb-replication`-plugin is now imported by default, do not import it by your own.
- `RxDB.create()` throws if you create the same database twice. (You can use [ignoreDuplicate](https://pubkey.github.io/rxdb/rx-database.html#ignoreduplicate))
Expand Down
1 change: 1 addition & 0 deletions docs-src/replication-graphql.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ const replicationState = myCollection.syncGraphQL({
pull: {
queryBuilder: pullQueryBuilder, // the queryBuilder from above
modifier: doc => doc // (optional) modifies all pulled documents before they are handeled by RxDB. Returning null will skip the document.
dataPath: undefined // (optional) specifies the object path to access the document(s). Otherwise, the first result of the response data is used.
},
deletedFlag: 'deleted', // the flag which indicates if a pulled document is deleted
live: true // if this is true, rxdb will watch for ongoing changes and sync them, when false, a one-time-replication will be done
Expand Down
6 changes: 3 additions & 3 deletions src/plugins/replication-graphql/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
filter
} from 'rxjs/operators';
import GraphQLClient from 'graphql-client';
import objectPath from 'object-path';
import {
promiseWait,
flatClone,
Expand Down Expand Up @@ -235,9 +236,8 @@ export class RxGraphQLReplicationState {
return false;
}

// this assumes that there will be always only one property in the response
// is this correct?
const data: any[] = result.data[Object.keys(result.data)[0]];
const dataPath = (this.pull as any).dataPath || ['data', Object.keys(result.data)[0]];
const data: any[] = objectPath.get(result, dataPath);
const modified: any[] = (await Promise.all(data
.map(async (doc: any) => await (this.pull as any).modifier(doc))
)).filter(doc => !!doc);
Expand Down
1 change: 1 addition & 0 deletions src/types/plugins/replication-graphql.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export type RxGraphQLReplicationQueryBuilder = (doc: any) =>
export interface GraphQLSyncPullOptions {
queryBuilder: RxGraphQLReplicationQueryBuilder;
modifier?: (doc: any) => Promise<any> | any;
dataPath?: string;
}
export interface GraphQLSyncPushOptions {
queryBuilder: RxGraphQLReplicationQueryBuilder;
Expand Down
24 changes: 20 additions & 4 deletions test/helper/graphql-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export async function spawn(
type Query {
info: Int
feedForRxDBReplication(lastId: String!, minUpdatedAt: Int!, limit: Int!): [Human!]!
collectionFeedForRxDBReplication(lastId: String!, minUpdatedAt: Int!, offset: Int, limit: Int!): HumanCollection!
getAll: [Human!]!
}
type Mutation {
Expand Down Expand Up @@ -118,6 +119,10 @@ export async function spawn(
_rev: String,
_revisions: Revision,
}
type HumanCollection {
collection: [Human!]
totalCount: Int!
}
type Subscription {
humanChanged: Human
}
Expand All @@ -138,25 +143,36 @@ export async function spawn(
// The root provides a resolver function for each API endpoint
const root = {
info: () => 1,
collectionFeedForRxDBReplication: (args: any) => {
const { limit, offset = 0, ...feedForRxDBReplicationArgs } = args;
const collection = root.feedForRxDBReplication(feedForRxDBReplicationArgs);

// console.log('collection');
// console.dir(collection);

return {
totalCount: collection.length,
collection: collection.slice(offset, offset + limit)
};
},
feedForRxDBReplication: (args: any) => {
// console.log('## feedForRxDBReplication');
// console.dir(args);
// sorted by updatedAt and primary
const sortedDocuments = documents.sort(sortByUpdatedAtAndPrimary);

// only return where updatedAt >= minUpdatedAt
const filterForMinUpdatedAtAndId = sortedDocuments.filter((doc) => {
const filteredByMinUpdatedAtAndId = sortedDocuments.filter((doc) => {
if (doc.updatedAt < args.minUpdatedAt) return false;
if (doc.updatedAt > args.minUpdatedAt) return true;
if (doc.updatedAt === args.minUpdatedAt) {
if (doc.id > args.lastId) return true;
else return false;
}

});

// limit
const limited = filterForMinUpdatedAtAndId.slice(0, args.limit);
// limit if requested
const limited = args.limit ? filteredByMinUpdatedAtAndId.slice(0, args.limit) : filteredByMinUpdatedAtAndId;

/*
console.log('sortedDocuments:');
Expand Down
57 changes: 57 additions & 0 deletions test/unit/replication-graphql.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,63 @@ describe('replication-graphql.test.js', () => {
server.close();
c.database.destroy();
});
it('should pull documents from a custom dataPath if one is specified', async () => {
const [c, server] = await Promise.all([
humansCollection.createHumanWithTimestamp(0),
SpawnServer.spawn(getTestData(batchSize))
]);

const collectionQueryBuilder = (doc: any) => {
if (!doc) {
doc = {
id: '',
updatedAt: 0
};
}

const query = `query($lastId: String!, $updatedAt: Int!, $batchSize: Int!)
{
collectionFeedForRxDBReplication(lastId: $lastId, minUpdatedAt: $updatedAt, limit: $batchSize) {
collection {
id
name
age
updatedAt
deleted
}
}
}`;

const variables = {
lastId: doc.id,
updatedAt: doc.updatedAt,
batchSize
};

return {
query,
variables
};
};

const replicationState = c.syncGraphQL({
url: server.url,
pull: {
queryBuilder: collectionQueryBuilder,
dataPath: 'data.collectionFeedForRxDBReplication.collection'
},
deletedFlag: 'deleted'
});
assert.strictEqual(replicationState.isStopped(), false);

await AsyncTestUtil.waitUntil(async () => {
const docs = await c.find().exec();
return docs.length === batchSize;
});

server.close();
c.database.destroy();
});
it('pulled docs should be marked with a special revision if syncRevisions is false', async () => {
const [c, server] = await Promise.all([
humansCollection.createHumanWithTimestamp(0),
Expand Down