Skip to content

Commit

Permalink
Add comments to hook stacks; the types look
Browse files Browse the repository at this point in the history
  • Loading branch information
jpwilliams committed Feb 29, 2024
2 parents 7138d3e + affbdae commit a8ef1ed
Show file tree
Hide file tree
Showing 8 changed files with 422 additions and 72 deletions.
6 changes: 6 additions & 0 deletions packages/inngest/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# inngest

## 3.15.2

### Patch Changes

- [#503](https://github.com/inngest/inngest-js/pull/503) [`f6088e0`](https://github.com/inngest/inngest-js/commit/f6088e0c04b5732c3b5e95c79f75c423625ba15d) Thanks [@jpwilliams](https://github.com/jpwilliams)! - Fix `onSendEvent.transformInput()` middleware hooks not running for `step.invoke()` payloads

## 3.15.1

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/inngest/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "inngest",
"version": "3.15.1",
"version": "3.15.2",
"description": "Official SDK for Inngest.com. Inngest is the reliability layer for modern applications. Inngest combines durable execution, events, and queues into a zero-infra platform with built-in observability.",
"main": "./index.js",
"types": "./index.d.ts",
Expand Down
38 changes: 1 addition & 37 deletions packages/inngest/src/components/InngestFunction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import {
type InngestExecutionOptions,
} from "@local/components/execution/InngestExecution";
import { _internals } from "@local/components/execution/v1";
import { ServerTiming } from "@local/helpers/ServerTiming";
import { internalEvents } from "@local/helpers/consts";
import {
ErrCode,
Expand All @@ -42,7 +41,7 @@ import {
} from "@local/types";
import { fromPartial } from "@total-typescript/shoehorn";
import { assertType, type IsEqual } from "type-plus";
import { createClient } from "../test/helpers";
import { createClient, runFnWithStack } from "../test/helpers";

type TestEvents = {
foo: { data: { foo: string } };
Expand Down Expand Up @@ -94,8 +93,6 @@ const opts = (<T extends ClientOptions>(x: T): T => x)({

const inngest = createClient(opts);

const timer = new ServerTiming();

const matchError = (err: any) => {
const serializedErr = serializeError(err);
return expect.objectContaining({
Expand Down Expand Up @@ -222,39 +219,6 @@ describe("runFn", () => {
});

describe("step functions", () => {
const runFnWithStack = (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
fn: InngestFunction.Any,
stepState: InngestExecutionOptions["stepState"],
opts?: {
executionVersion?: ExecutionVersion;
runStep?: string;
onFailure?: boolean;
event?: EventPayload;
stackOrder?: InngestExecutionOptions["stepCompletionOrder"];
disableImmediateExecution?: boolean;
}
) => {
const execution = fn["createExecution"]({
version: opts?.executionVersion ?? PREFERRED_EXECUTION_VERSION,
partialOptions: {
data: fromPartial({
event: opts?.event || { name: "foo", data: {} },
}),
runId: "run",
stepState,
stepCompletionOrder: opts?.stackOrder ?? Object.keys(stepState),
isFailureHandler: Boolean(opts?.onFailure),
requestedRunStep: opts?.runStep,
timer,
disableImmediateExecution: opts?.disableImmediateExecution,
reqArgs: [],
},
});

return execution.start();
};

const getHashDataSpy = () => jest.spyOn(_internals, "hashOp");
const getWarningSpy = () => jest.spyOn(console, "warn");
const getErrorSpy = () => jest.spyOn(console, "error");
Expand Down
250 changes: 250 additions & 0 deletions packages/inngest/src/components/InngestMiddleware.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ExecutionVersion } from "@local/components/execution/InngestExecution";
import { Inngest } from "@local/components/Inngest";
import { referenceFunction } from "@local/components/InngestFunctionReference";
import { InngestMiddleware } from "@local/components/InngestMiddleware";
import { type IsUnknown } from "@local/helpers/types";
import { StepOpCode } from "@local/types";
import { assertType, type IsEqual } from "type-plus";
import { createClient, runFnWithStack, testClientId } from "../test/helpers";

describe("stacking and inference", () => {
describe("onFunctionRun", () => {
Expand Down Expand Up @@ -260,6 +265,251 @@ describe("stacking and inference", () => {
});

describe("onSendEvent", () => {
describe("transformInput", () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const mockFetch = jest.fn(() =>
Promise.resolve({
status: 200,
json: () => Promise.resolve({ ids: [], status: 200 }),
text: () => Promise.resolve(""),
})
) as any;

beforeEach(() => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
mockFetch.mockClear();
});

describe("step.invoke()", () => {
test("returning a new payload overwrites the original", async () => {
const fn = createClient({
id: testClientId,
middleware: [
new InngestMiddleware({
name: "Test: onSendEvent.transformInput",
init() {
return {
onSendEvent() {
return {
transformInput() {
return {
payloads: [
{
name: "foo",
data: { dataFromMiddleware: true },
},
],
};
},
};
},
};
},
}),
],
}).createFunction(
{ id: "fn_id" },
{ event: "foo" },
async ({ step }) => {
await step.invoke("id", {
function: referenceFunction({
functionId: "some_fn_id",
data: { dataFromStep: true },
}),
});
}
);

const res = await runFnWithStack(
fn,
{},
{ executionVersion: ExecutionVersion.V1 }
);

expect(res).toMatchObject({
steps: [
expect.objectContaining({
op: StepOpCode.InvokeFunction,
opts: expect.objectContaining({
payload: {
data: { dataFromMiddleware: true },
},
}),
}),
],
});
});

test("returning no payload keeps the original", async () => {
const fn = createClient({
id: testClientId,
middleware: [
new InngestMiddleware({
name: "Test: onSendEvent.transformInput",
init() {
return {
onSendEvent() {
return {
transformInput() {
return {
payloads: [],
};
},
};
},
};
},
}),
],
}).createFunction(
{ id: "fn_id" },
{ event: "foo" },
async ({ step }) => {
await step.invoke("id", {
function: referenceFunction({
functionId: "some_fn_id",
}),
data: { dataFromStep: true },
});
}
);

const res = await runFnWithStack(
fn,
{},
{ executionVersion: ExecutionVersion.V1 }
);

expect(res).toMatchObject({
steps: [
expect.objectContaining({
op: StepOpCode.InvokeFunction,
opts: expect.objectContaining({
payload: {
data: { dataFromStep: true },
},
}),
}),
],
});
});

test("returning a partial payload merges with the original, preferring the new value", async () => {
const fn = createClient({
id: testClientId,
middleware: [
new InngestMiddleware({
name: "Test: onSendEvent.transformInput",
init() {
return {
onSendEvent() {
return {
transformInput() {
return {
payloads: [
{
name: "foo",
user: { userFromMiddleware: true },
},
],
};
},
};
},
};
},
}),
],
}).createFunction(
{ id: "fn_id" },
{ event: "foo" },
async ({ step }) => {
await step.invoke("id", {
function: referenceFunction({
functionId: "some_fn_id",
}),
data: { dataFromStep: true },
});
}
);

const res = await runFnWithStack(
fn,
{},
{ executionVersion: ExecutionVersion.V1 }
);

expect(res).toMatchObject({
steps: [
expect.objectContaining({
op: StepOpCode.InvokeFunction,
opts: expect.objectContaining({
payload: {
data: {
dataFromStep: true,
},
user: {
userFromMiddleware: true,
},
},
}),
}),
],
});
});

test("hook runs once per invocation", async () => {
const transformInputSpy = jest.fn(() => undefined);

const onSendEventSpy = jest.fn(() => ({
transformInput: transformInputSpy,
}));

const fn = createClient({
id: testClientId,
middleware: [
new InngestMiddleware({
name: "Test: onSendEvent.transformInput",
init() {
return {
onSendEvent: onSendEventSpy,
};
},
}),
],
}).createFunction(
{ id: "fn_id" },
{ event: "foo" },
async ({ step }) => {
await Promise.all([
step.invoke("id", {
function: referenceFunction({
functionId: "some_fn_id",
data: { dataFromStep: true },
}),
}),
step.invoke("id", {
function: referenceFunction({
functionId: "some_fn_id",
data: { dataFromStep: true },
}),
}),
]);
}
);

await runFnWithStack(
fn,
{},
{ executionVersion: ExecutionVersion.V1 }
);

expect(onSendEventSpy).toHaveBeenCalledTimes(2);
expect(transformInputSpy).toHaveBeenCalledTimes(2);
});
});
});

describe("transformOutput", () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const mockFetch = jest.fn(() =>
Expand Down

0 comments on commit a8ef1ed

Please sign in to comment.