Skip to content

Commit

Permalink
fix: passthrough output if it's a reference
Browse files Browse the repository at this point in the history
  • Loading branch information
zirkelc committed Oct 17, 2024
1 parent d337fea commit aad227d
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .changeset/five-oranges-protect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"middy-store": patch
---

fix: passthrough output if it's a reference
25 changes: 21 additions & 4 deletions packages/core/src/store.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { MiddlewareObj } from "@middy/core";
import {
calculateByteSize,
createReference,
formatPath,
generatePayloadPaths,
generateReferencePaths,
Expand Down Expand Up @@ -68,12 +69,16 @@ export const Sizes = {
LAMBDA_ASYNC: 256 * 1024 * 1024, // 256KB,
};

// TODO add metadata object which will be passed to the store and load methods

export type StoreArgs<TPayload> = {
// TODO add output to StoreArgs OR metadata?
payload: TPayload;
byteSize: number;
};

export type LoadArgs<TReference> = {
// TODO add input to LoadArgs OR metadata?
reference: TReference;
};

Expand Down Expand Up @@ -105,9 +110,13 @@ export interface LoadingOptions<TInput> {
passThrough?: boolean;

// selector?: Selector<TInput>; // TODO

// TODO metadata here?
}

export interface StoringOptions<TInput, TOutput> {
// TODO metadata here?

/**
* Skip storing the payload in the Store, even if the output exceeds the maximum size.
*/
Expand Down Expand Up @@ -190,7 +199,9 @@ export const middyStore = <TInput = unknown, TOutput = unknown>(
const { stores, loadingOptions, storingOptions } = opts;
const logger = opts.logger ?? DUMMY_LOGGER;

return {
// let request: Request<TInput, TOutput> | undefined;

const middleware: MiddlewareObj<TInput, TOutput> = {
before: async (request) => {
// setting read to false will skip the store
if (loadingOptions?.skip) {
Expand Down Expand Up @@ -339,9 +350,9 @@ export const middyStore = <TInput = unknown, TOutput = unknown>(
logger(`Found store "${store.name}" to save payload`);

// store the payload in the store
const reference: MiddyStore<unknown> = {
[MIDDY_STORE]: await store.store(storeArgs),
};
const reference: MiddyStore<unknown> = createReference(
await store.store(storeArgs),
);

logger(`Saved payload in store "${store.name}"`);

Expand All @@ -361,4 +372,10 @@ export const middyStore = <TInput = unknown, TOutput = unknown>(
}
},
};

// const earlyReturn = (output: TOutput) => {
// middleware.after!({});
// };

return middleware;
};
11 changes: 11 additions & 0 deletions packages/core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ export function* generatePayloadPaths({
throw new Error(`Path not found at ${path}`);
}

// if the payload is a reference, we skip it
if (hasReference(payload)) {
return;
}

if (isMultiPayload && Array.isArray(payload)) {
for (let index = 0; index < payload.length; index++) {
// if the item is a reference, we skip it
Expand All @@ -197,6 +202,12 @@ export const getReference = <TReference = unknown>(
return hasReference<TReference>(input) ? input[MIDDY_STORE] : undefined;
};

export const createReference = <TReference = unknown>(
reference: TReference,
): MiddyStore<TReference> => {
return { [MIDDY_STORE]: reference };
};

type GenerateReferencePathsArgs = {
input: unknown;
path: string;
Expand Down
137 changes: 124 additions & 13 deletions packages/core/test/store.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
type StoreInterface,
middyStore,
} from "../src/store.js";
import { calculateByteSize } from "../src/utils.js";
import { calculateByteSize, createReference } from "../src/utils.js";

const context = {} as Context;

Expand Down Expand Up @@ -520,12 +520,123 @@ describe("store", () => {

await expect(
handler(structuredClone([payload, payload]), context),
).resolves.toEqual({ [MIDDY_STORE]: reference });
).resolves.toEqual(createReference(reference));
expect(mockStore.canStore).toHaveBeenCalled();
expect(mockStore.store).toHaveBeenCalled();
});
});

test("should passthrough output if it's a reference", async () => {
const reference = { store: "mock" };

{
const payload = createReference(reference);
const handler = useStore({
stores: [mockStore],
loadingOptions: { skip: true },
storingOptions: {
minSize: Sizes.ZERO,
},
});

await expect(handler(payload, context)).resolves.toEqual(payload);
expect(mockStore.canStore).not.toHaveBeenCalled();
expect(mockStore.store).not.toHaveBeenCalled();
}

{
const payload = { a: createReference(reference) };
const handler = useStore({
stores: [mockStore],
loadingOptions: { skip: true },
storingOptions: {
minSize: Sizes.ZERO,
selector: "a",
},
});

await expect(handler(payload, context)).resolves.toEqual(payload);
expect(mockStore.canStore).not.toHaveBeenCalled();
expect(mockStore.store).not.toHaveBeenCalled();
}

{
const payload = { a: { b: createReference(reference) } };
const handler = useStore({
stores: [mockStore],
loadingOptions: { skip: true },
storingOptions: {
minSize: Sizes.ZERO,
selector: "a.b",
},
});

await expect(handler(payload, context)).resolves.toEqual(payload);
expect(mockStore.canStore).not.toHaveBeenCalled();
expect(mockStore.store).not.toHaveBeenCalled();
}

{
const payload = {
a: {
b: { c: createReference(reference) },
},
};
const handler = useStore({
stores: [mockStore],
loadingOptions: { skip: true },
storingOptions: {
minSize: Sizes.ZERO,
selector: "a.b.c",
},
});

await expect(handler(payload, context)).resolves.toEqual(payload);
expect(mockStore.canStore).not.toHaveBeenCalled();
expect(mockStore.store).not.toHaveBeenCalled();
}

{
const payload = {
a: {
b: { c: [createReference(reference), createReference(reference)] },
},
};
const handler = useStore({
stores: [mockStore],
loadingOptions: { skip: true },
storingOptions: {
minSize: Sizes.ZERO,
selector: "a.b.c[0]",
},
});

await expect(handler(payload, context)).resolves.toEqual(payload);
expect(mockStore.canStore).not.toHaveBeenCalled();
expect(mockStore.store).not.toHaveBeenCalled();
}

{
const payload = {
a: {
b: { c: [createReference(reference), createReference(reference)] },
},
};
const handler = useStore({
stores: [mockStore],
loadingOptions: { skip: true },
storingOptions: {
minSize: Sizes.ZERO,
selector: "a.b.c[*]",
},
});

await expect(handler(payload, context)).resolves.toEqual(payload);
expect(mockStore.canStore).not.toHaveBeenCalled();
expect(mockStore.store).not.toHaveBeenCalled();
}
});

test("should passthrough output if stores are empty", async () => {
const payload = {
foo: "bar",
Expand Down Expand Up @@ -694,7 +805,7 @@ describe("store", () => {

await expect(
handler(structuredClone(payload), context),
).resolves.toEqual({ [MIDDY_STORE]: reference });
).resolves.toEqual(createReference(reference));
expect(mockStore.canStore).toHaveBeenCalledWith({ payload, byteSize });
expect(mockStore.store).toHaveBeenCalledWith({ payload, byteSize });
}
Expand All @@ -710,7 +821,7 @@ describe("store", () => {

await expect(
handler(structuredClone(payload), context),
).resolves.toEqual({ [MIDDY_STORE]: reference });
).resolves.toEqual(createReference(reference));
expect(mockStore.canStore).toHaveBeenCalledWith({ payload, byteSize });
expect(mockStore.store).toHaveBeenCalledWith({ payload, byteSize });
}
Expand All @@ -727,7 +838,7 @@ describe("store", () => {

await expect(
handler(structuredClone(payload), context),
).resolves.toEqual({ [MIDDY_STORE]: reference });
).resolves.toEqual(createReference(reference));
expect(mockStore.canStore).toHaveBeenCalledWith({ payload, byteSize });
expect(mockStore.store).toHaveBeenCalledWith({ payload, byteSize });
}
Expand All @@ -744,7 +855,7 @@ describe("store", () => {

await expect(
handler(structuredClone(payload), context),
).resolves.toEqual({ [MIDDY_STORE]: reference });
).resolves.toEqual(createReference(reference));
expect(mockStore.canStore).toHaveBeenCalledWith({ payload, byteSize });
expect(mockStore.store).toHaveBeenCalledWith({ payload, byteSize });
}
Expand All @@ -771,7 +882,7 @@ describe("store", () => {

await expect(
handler(structuredClone(payload), context),
).resolves.toEqual({ [MIDDY_STORE]: reference });
).resolves.toEqual(createReference(reference));
expect(mockStore.canStore).toHaveBeenCalledWith({ payload, byteSize });
expect(mockStore.store).toHaveBeenCalledWith({ payload, byteSize });
}
Expand All @@ -787,7 +898,7 @@ describe("store", () => {

await expect(
handler(structuredClone(payload), context),
).resolves.toEqual({ [MIDDY_STORE]: reference });
).resolves.toEqual(createReference(reference));
expect(mockStore.canStore).toHaveBeenCalledWith({ payload, byteSize });
expect(mockStore.store).toHaveBeenCalledWith({ payload, byteSize });
}
Expand All @@ -803,7 +914,7 @@ describe("store", () => {

await expect(
handler(structuredClone(payload), context),
).resolves.toEqual({ a: { [MIDDY_STORE]: reference } });
).resolves.toEqual({ a: createReference(reference) });
expect(mockStore.canStore).toHaveBeenCalledWith({ payload, byteSize });
expect(mockStore.store).toHaveBeenCalledWith({ payload, byteSize });
}
Expand All @@ -819,7 +930,7 @@ describe("store", () => {

await expect(
handler(structuredClone(payload), context),
).resolves.toEqual({ a: { b: { [MIDDY_STORE]: reference } } });
).resolves.toEqual({ a: { b: createReference(reference) } });
expect(mockStore.canStore).toHaveBeenCalledWith({ payload, byteSize });
expect(mockStore.store).toHaveBeenCalledWith({ payload, byteSize });
}
Expand All @@ -835,7 +946,7 @@ describe("store", () => {

await expect(
handler(structuredClone(payload), context),
).resolves.toEqual({ a: { b: { c: { [MIDDY_STORE]: reference } } } });
).resolves.toEqual({ a: { b: { c: createReference(reference) } } });
expect(mockStore.canStore).toHaveBeenCalledWith({ payload, byteSize });
expect(mockStore.store).toHaveBeenCalledWith({ payload, byteSize });
}
Expand All @@ -852,7 +963,7 @@ describe("store", () => {
await expect(
handler(structuredClone(payload), context),
).resolves.toEqual({
a: { b: { c: [{ [MIDDY_STORE]: reference }, { foo: "bar" }] } },
a: { b: { c: [createReference(reference), { foo: "bar" }] } },
});
expect(mockStore.canStore).toHaveBeenCalledWith({ payload, byteSize });
expect(mockStore.store).toHaveBeenCalledWith({ payload, byteSize });
Expand All @@ -872,7 +983,7 @@ describe("store", () => {
).resolves.toEqual({
a: {
b: {
c: [{ [MIDDY_STORE]: reference }, { [MIDDY_STORE]: reference }],
c: [createReference(reference), createReference(reference)],
},
},
});
Expand Down
9 changes: 9 additions & 0 deletions packages/core/test/utils.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { randomStringInBytes } from "../src/internal.js";
import { MIDDY_STORE, Sizes } from "../src/store.js";
import {
calculateByteSize,
createReference,
hasReference,
isObject,
resolvableFn,
Expand Down Expand Up @@ -164,3 +165,11 @@ describe("getReference", () => {
},
);
});

describe("createReference", () => {
test("should create a reference", async () => {
const reference = "foo";
const result = createReference(reference);
expect(result).toEqual({ [MIDDY_STORE]: reference });
});
});

0 comments on commit aad227d

Please sign in to comment.