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

Fix hydrate onupdate not working without query #2

Merged
merged 5 commits into from
Dec 12, 2020
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
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Changelog

## Unreleased

### 0.2.0

#### Fixed

- `onUpdate` now only called if `query` is present in `hydrate` (#2)
- `triggerListeners` now called with correct data in `hydrate` (#2)
- correct response returned if `query` is not set (#2)

### Released

### 0.1.1
...
### 0.1.0
...
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Hyperfast data management to enable next-gen UX",
"main": "dist/bundle.umd.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"test": "jest",
"package": "rollup -c",
"package-watch": "rollup -c --watch",
"minify": "uglifyjs dist/bundle.umd.js --compress --mangle --comments --output dist/bundle.umd.min.js",
Expand Down
178 changes: 178 additions & 0 deletions src/hux-api/application/__tests__/hydrateRequest.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import { hydrateRequest } from "../hydrateRequest";

import {
triggerListeners,
query,
listenToBucket,
} from "../../../hux-store";

const flushPromises = () => new Promise((resolve) => process.nextTick(resolve));

jest.mock('../../../hux-workers', () => ({
createWorker: jest.fn().mockReturnValue(Promise.resolve({
dispatcher: jest.fn().mockReturnValue(Promise.resolve({}))
})),
WorkerEvent: {
HYDRATE: 'HYDRATE'
}
}))

jest.mock('../../../hux-store', () => ({
query: jest.fn().mockReturnValue(Promise.resolve({})),
listenToBucket: jest.fn(),
triggerListeners: jest.fn()
}))

describe("hydrateRequest", () => {
const defaultRequest = {
url: "http://localhost",
options: {},
aggregations: [],
schema: {},
name: "mock-bucket",
};

afterEach(() => {
jest.clearAllMocks();
});

describe("Given a basic hydrate request", () => {
const request = {
...defaultRequest,
};

describe("When hydrateRequest is called", () => {
let result;

beforeEach(async () => {
result = await hydrateRequest(request);
})

test("Then the listeners are triggered with the correct data", () => {
expect(triggerListeners).toHaveBeenCalledWith({
name: "mock-bucket",
data: {}
});
});

test("Then listenToBucket is NOT called", () => {
expect(listenToBucket).not.toHaveBeenCalled();
});

test("Then the correct server response is returned", () => {
expect(result).toEqual({
metrics: {
details: {
dataFrom: "Server",
},
},
result: {},
});
});
});
});

describe("Given a hydrate request with query and a listener", () => {
const request = {
...defaultRequest,
query: [],
onUpdate: () => {},
};

describe("When hydrateRequest is called and cache is available", () => {
let result;

beforeEach(async () => {
query
.mockReturnValueOnce(Promise.resolve({ foo: 'cached' }))
.mockReturnValueOnce(Promise.resolve({ foo: 'server' }))
result = await hydrateRequest(request);

await flushPromises();
})

test("Then the listeners are triggered with the correct data for both cache and server", () => {
expect(triggerListeners.mock.calls).toEqual([
[{
name: "mock-bucket",
data: {
foo: 'cached'
}
}],
[{
name: "mock-bucket",
data: {
foo: 'server'
}
}]
])
});

test("Then listenToBucket is called with the correct data", () => {
expect(listenToBucket).toHaveBeenCalledWith({
name: "mock-bucket",
onUpdate: request.onUpdate,
query: []
});
});

test("Then the correct cached response is returned", () => {
expect(result).toEqual({
metrics: {
details: {
dataFrom: "Cache",
},
},
result: {
foo: 'cached'
},
});
});
});

describe("When hydrateRequest is called and cache is NOT available", () => {
let result;

beforeEach(async () => {
query
.mockReturnValueOnce(Promise.resolve())
.mockReturnValueOnce(Promise.resolve({ foo: 'server' }))
result = await hydrateRequest(request);

await flushPromises();
})

test("Then the listeners are triggered with the correct data for just server", () => {
expect(triggerListeners.mock.calls).toEqual([
[{
name: "mock-bucket",
data: {
foo: 'server'
}
}]
])
});

test("Then listenToBucket is called with the correct data", () => {
expect(listenToBucket).toHaveBeenCalledWith({
name: "mock-bucket",
onUpdate: request.onUpdate,
query: []
});
});

test("Then the correct server response is returned", () => {
expect(result).toEqual({
metrics: {
details: {
dataFrom: "Server",
},
},
result: {
foo: 'server'
},
});
});
});
});
});
62 changes: 43 additions & 19 deletions src/hux-api/application/hydrateRequest.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
import { createWorker, WorkerEvent } from "../../hux-workers";
import { triggerListeners, query as queryBucket } from "../../hux-store";
import {
triggerListeners,
query as queryBucket,
listenToBucket,
} from "../../hux-store";

const hydrateServerRequest = async ({ request, query }) => {
const worker = await createWorker();

await worker.dispatcher(JSON.stringify(request));

const queryResponse = await queryBucket({
name: request.name,
query: query || [],
});

const response = {
result: queryResponse,
metrics: { details: { dataFrom: "Server" } },
};

return response;
};

const hydrateRequest = async ({
url,
Expand All @@ -12,27 +34,25 @@ const hydrateRequest = async ({
onUpdate,
}) => {
let response;
const worker = await createWorker();

const stringifiedAggregations = aggregations.map((aggregation) =>
aggregation.toString()
);

const request = JSON.stringify({
const request = {
eventType: WorkerEvent.HYDRATE,
url,
options,
aggregations: stringifiedAggregations,
schema,
name,
hasKey,
});
};

if (query) {
const cachedQueryResponse = await queryBucket({
name,
query,
onUpdate,
});

if (cachedQueryResponse) {
Expand All @@ -41,28 +61,32 @@ const hydrateRequest = async ({
metrics: { details: { dataFrom: "Cache" } },
};

// Set up async revalidation so we can return cached response early
(async () => {
const revalidatedResponse = JSON.parse(
await worker.dispatcher(request)
);
const queryResponse = await hydrateServerRequest({
request,
query,
});

triggerListeners({ name, data: revalidatedResponse });
triggerListeners({ name, data: queryResponse.result });
})();
} else {
await worker.dispatcher(request);

const syncQueryResponse = await queryBucket({ name, query, onUpdate });

response = {
result: syncQueryResponse,
metrics: { details: { dataFrom: "Server" } },
};
response = await hydrateServerRequest({
request,
query,
});
}
} else {
response = JSON.parse(await worker.dispatcher(request));
response = await hydrateServerRequest({ request });
}

triggerListeners({ name, data: response });
triggerListeners({ name, data: response.result });

// Set onUpdate after triggerListeners
// so we don't duplicate the data response
if (onUpdate && query) {
listenToBucket({ name, onUpdate, query });
}

return response;
};
Expand Down
6 changes: 3 additions & 3 deletions src/hux-api/interface/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const hydrate = async ({ name, query, aggregations = [], onUpdate }) => {
console.error(
generateError({
type: errors.MISSING_REQUIRED_PARAM,
details: { param: 'name', action: "hydrate" },
details: { param: "name", action: "hydrate" },
})
);

Expand Down Expand Up @@ -46,15 +46,15 @@ const hydrate = async ({ name, query, aggregations = [], onUpdate }) => {
response = await hydrateRequest(params);
}

return response;
return query ? response : null;
};

const sync = async ({ name, mode, data, fromProfiler }) => {
if (!name) {
console.error(
generateError({
type: errors.MISSING_REQUIRED_PARAM,
details: { param: 'name', action: "sync" },
details: { param: "name", action: "sync" },
})
);

Expand Down
2 changes: 1 addition & 1 deletion src/hux-ql/interface/ql.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const Filter = (id, filter, page, limit) => {
console.error(
generateError({
type: errors.MISSING_REQUIRED_PARAM,
details: { param: 'id or filter', action: "Filter" },
details: { param: "id or filter", action: "Filter" },
})
);

Expand Down
2 changes: 1 addition & 1 deletion src/hux-store/application/initialiseBucketCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ const initialiseBucketCommand = async ({ name, schema, hasKey }) => {
);
};

export { initialiseBucketCommand };
export { initialiseBucketCommand };
2 changes: 1 addition & 1 deletion src/hux-store/application/listenToBucketCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ const listenToBucketCommand = async ({ name, onUpdate, query }) => {
Store.buckets[name].listeners.push(processedListener);
};

export { listenToBucketCommand };
export { listenToBucketCommand };
2 changes: 1 addition & 1 deletion src/hux-store/application/queryRequest.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createWorker, WorkerEvent } from "../../hux-workers";
import { listenToBucket } from '../';
import { listenToBucket } from "../";

const queryRequest = async ({ name, query, onUpdate }) => {
const worker = await createWorker();
Expand Down
2 changes: 1 addition & 1 deletion src/hux-store/application/triggerListenersCommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ const triggerListenersCommand = async ({ name, data }) => {
}
};

export { triggerListenersCommand };
export { triggerListenersCommand };
10 changes: 6 additions & 4 deletions src/hux-store/domain/Bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ function Bucket({ hydrate, sync, schema, name }) {
const { validateSchema, valid, hasKey } = compileSchema({ schema });

if (!valid) {
console.error(generateError({
type: errors.INVALID_SCHEMA,
details: validateSchema,
}));
console.error(
generateError({
type: errors.INVALID_SCHEMA,
details: validateSchema,
})
);

return;
}
Expand Down
Loading