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

Add new hooks in the replication write #4754

Merged
merged 3 commits into from
Jun 2, 2023

Conversation

jonathan-neugber
Copy link
Contributor

This PR contains:

  • Two new hooks

Describe the problem you have without this PR

We'd like to have local fields in a row that are not replicated to the server.
These hooks allow us to modify the replication a bit to filter out rows that do not need to be replicated or need to be updated after replication to include the local fields again.

I'm just not really sure how to best write unit tests for this but help/feedback would be greatly appreciated

An example plugin I wrote to handle this:

import {RxCollection, RxDocType, RxPlugin, RxReplicationWriteToMasterRow, WithDeleted} from "rxdb/src/types";
import {deepEqual} from "rxdb";

let rowsToUpdatePerSchema = {};

export const LocalFieldsPlugin: RxPlugin = {
    name: 'LocalFields',
    rxdb: true,
    hooks: {
        preReplicationMasterWrite: {
            after: async ({rows, collection}: {rows: RxReplicationWriteToMasterRow<RxDocType>[], collection: RxCollection<RxDocType>})  => {
                const name = collection.name;
                const localFields = collection.schema.jsonSchema.local || [];

                if (localFields.lenght === 0) {
                    return;
                }

                const rowsToUpdate = {};

                const filtered = rows.filter((row) => {
                    let localFieldChanged = false;
                    let serverFieldChanged = false;

                    const localChanges = {};

                    for (const [key, value] of Object.entries(row.newDocumentState)) {
                        const masterValue = row.assumedMasterState[key] ?? null;

                        if (deepEqual(value, masterValue)) {
                            continue;
                        }

                        if (localFields.includes(key)) {
                            localChanges[key] = value;
                            localFieldChanged = true;
                            delete row.newDocumentState[key];

                            continue;
                        }

                        serverFieldChanged = true;
                    }

                    if (localFieldChanged === true && serverFieldChanged === false) {
                        return false;
                    } else if (localFieldChanged) {
                        rowsToUpdate[row.newDocumentState.id] = localChanges;
                    }

                    return true;
                });

                rowsToUpdatePerSchema[name] = rowsToUpdate;

                rows.splice(0, rows.length);
                rows.push(...filtered);
            }
        },
        preReplicationMasterWriteDocumentsHandle: {
            after: async ({result, collection}: {result: WithDeleted<RxDocType>[], collection: RxCollection<RxDocType>}) => {
                const localFields = collection.schema.jsonSchema.local || [];

                if (localFields.lenght === 0) {
                    return;
                }

                const name = collection.name;

                const rowsToUpdate = rowsToUpdatePerSchema[name];

                if (!rowsToUpdate) {
                    return;
                }

                const mapped = result.map((row) => {
                    const updateData = rowsToUpdate[row.id];

                    if (!updateData) {
                        return row;
                    }

                    return {
                        ...row,
                        ...updateData,
                    }
                });

                result.splice(0, result.length);
                result.push(...mapped);
            }
        }
    }
};

Todos

  • Tests
  • Documentation

@pubkey
Copy link
Owner

pubkey commented Jun 1, 2023

Code looks good. But the CI fails, please check.

@jonathan-neugber
Copy link
Contributor Author

Turns out my local machine had some configuration problems in which eslint didn't correctly work. Now it should finally pass 😅

@jonathan-neugber jonathan-neugber changed the title DRAFT: Add new hooks in the replication write Add new hooks in the replication write Jun 2, 2023
@pubkey pubkey merged commit a73c91a into pubkey:master Jun 2, 2023
@pubkey
Copy link
Owner

pubkey commented Jun 2, 2023

Great, merged.

pubkey added a commit that referenced this pull request Jun 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants