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

Return event IDs when sending events #195

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/unlucky-snails-attack.md
@@ -0,0 +1,5 @@
---
"inngest": minor
---

Return event IDs when sending events using `inngest.send()` and `step.sendEvent()`
4 changes: 2 additions & 2 deletions etc/inngest.api.md
Expand Up @@ -106,9 +106,9 @@ export class Inngest<Events extends Record<string, EventPayload> = Record<string
readonly name: string;
// Warning: (ae-forgotten-export) The symbol "SingleOrArray" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "PartialK" needs to be exported by the entry point index.d.ts
send<Event extends keyof Events>(name: Event, payload: SingleOrArray<PartialK<Omit<Events[Event], "name" | "v">, "ts">>): Promise<void>;
send<Event extends keyof Events>(name: Event, payload: SingleOrArray<PartialK<Omit<Events[Event], "name" | "v">, "ts">>): Promise<string[]>;
// Warning: (ae-forgotten-export) The symbol "SendEventPayload" needs to be exported by the entry point index.d.ts
send<Payload extends SendEventPayload<Events>>(payload: Payload): Promise<void>;
send<Payload extends SendEventPayload<Events>>(payload: Payload): Promise<string[]>;
setEventKey(
eventKey: string): void;
}
Expand Down
26 changes: 15 additions & 11 deletions src/components/Inngest.test.ts
Expand Up @@ -59,10 +59,14 @@ describe("send", () => {
beforeAll(() => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
global.fetch = jest.fn(
() =>
(input: unknown) =>
Promise.resolve({
status: 200,
json: () => Promise.resolve({}),
json: () =>
Promise.resolve({
ids: Array.isArray(input) ? input.map((_, i) => `${i}`) : ["0"],
Copy link
Member

Choose a reason for hiding this comment

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

Seeing this and the test below, I noticed we may not have a test w/ an array of multiple events, but we do have one w/ an array. Is that something we should cover?

status: 200,
}),
})
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) as any;
Expand Down Expand Up @@ -90,7 +94,7 @@ describe("send", () => {
test("should succeed if event key specified at instantiation", async () => {
const inngest = createClient({ name: "test", eventKey: testEventKey });

await expect(inngest.send(testEvent)).resolves.toBeUndefined();
await expect(inngest.send(testEvent)).resolves.toEqual(["0"]);

expect(global.fetch).toHaveBeenCalledWith(
expect.stringContaining(`/e/${testEventKey}`),
Expand All @@ -105,7 +109,7 @@ describe("send", () => {
process.env[envKeys.EventKey] = testEventKey;
const inngest = createClient({ name: "test" });

await expect(inngest.send(testEvent)).resolves.toBeUndefined();
await expect(inngest.send(testEvent)).resolves.toEqual(["0"]);

expect(global.fetch).toHaveBeenCalledWith(
expect.stringContaining(`/e/${testEventKey}`),
Expand All @@ -120,7 +124,7 @@ describe("send", () => {
const inngest = createClient({ name: "test" });
inngest.setEventKey(testEventKey);

await expect(inngest.send(testEvent)).resolves.toBeUndefined();
await expect(inngest.send(testEvent)).resolves.toEqual(["0"]);

expect(global.fetch).toHaveBeenCalledWith(
expect.stringContaining(`/e/${testEventKey}`),
Expand All @@ -135,15 +139,15 @@ describe("send", () => {
const inngest = createClient({ name: "test" });
inngest.setEventKey(testEventKey);

await expect(inngest.send("test", [])).resolves.toBeUndefined();
await expect(inngest.send("test", [])).resolves.toEqual([]);
expect(global.fetch).not.toHaveBeenCalled();
});

test("should succeed if an empty list of payloads is given", async () => {
const inngest = createClient({ name: "test" });
inngest.setEventKey(testEventKey);

await expect(inngest.send([])).resolves.toBeUndefined();
await expect(inngest.send([])).resolves.toEqual([]);
expect(global.fetch).not.toHaveBeenCalled();
});

Expand All @@ -154,7 +158,7 @@ describe("send", () => {
env: "foo",
});

await expect(inngest.send(testEvent)).resolves.toBeUndefined();
await expect(inngest.send(testEvent)).resolves.toEqual(["0"]);
expect(global.fetch).toHaveBeenCalledWith(
expect.stringContaining(`/e/${testEventKey}`),
expect.objectContaining({
Expand All @@ -175,7 +179,7 @@ describe("send", () => {
eventKey: testEventKey,
});

await expect(inngest.send(testEvent)).resolves.toBeUndefined();
await expect(inngest.send(testEvent)).resolves.toEqual(["0"]);
expect(global.fetch).toHaveBeenCalledWith(
expect.stringContaining(`/e/${testEventKey}`),
expect.objectContaining({
Expand All @@ -197,7 +201,7 @@ describe("send", () => {
env: "foo",
});

await expect(inngest.send(testEvent)).resolves.toBeUndefined();
await expect(inngest.send(testEvent)).resolves.toEqual(["0"]);
expect(global.fetch).toHaveBeenCalledWith(
expect.stringContaining(`/e/${testEventKey}`),
expect.objectContaining({
Expand All @@ -218,7 +222,7 @@ describe("send", () => {
eventKey: testEventKey,
});

await expect(inngest.send(testEvent)).resolves.toBeUndefined();
await expect(inngest.send(testEvent)).resolves.toEqual(["0"]);
expect(global.fetch).toHaveBeenCalledWith(
expect.stringContaining(`/e/${testEventKey}`),
expect.objectContaining({
Expand Down
34 changes: 24 additions & 10 deletions src/components/Inngest.ts
@@ -1,3 +1,4 @@
import { z } from "zod";
import { envKeys } from "../helpers/consts";
import { devServerAvailable, devServerUrl } from "../helpers/devserver";
import {
Expand Down Expand Up @@ -148,7 +149,7 @@ export class Inngest<
/**
* Given a response from Inngest, relay the error to the caller.
*/
async #getResponseError(response: globalThis.Response): Promise<Error> {
#getResponseError(response: globalThis.Response, rawBody: unknown): Error {
let errorMessage = "Unknown error";
switch (response.status) {
case 401:
Expand All @@ -164,7 +165,7 @@ export class Inngest<
errorMessage = "Event key not found";
break;
case 406:
errorMessage = `${JSON.stringify(await response.json())}`;
errorMessage = `${JSON.stringify(rawBody)}`;
break;
case 409:
case 412:
Expand Down Expand Up @@ -224,7 +225,7 @@ export class Inngest<
public async send<Event extends keyof Events>(
name: Event,
payload: SingleOrArray<PartialK<Omit<Events[Event], "name" | "v">, "ts">>
): Promise<void>;
): Promise<string[]>;
/**
* Send one or many events to Inngest. Takes an entire payload (including
* name) as each input.
Expand All @@ -251,7 +252,7 @@ export class Inngest<
*/
public async send<Payload extends SendEventPayload<Events>>(
payload: Payload
): Promise<void>;
): Promise<string[]>;
public async send<Event extends keyof Events>(
nameOrPayload:
| Event
Expand All @@ -263,7 +264,7 @@ export class Inngest<
maybePayload?: SingleOrArray<
PartialK<Omit<Events[Event], "name" | "v">, "ts">
>
): Promise<void> {
): Promise<string[]> {
if (!this.eventKey) {
throw new Error(
prettyError({
Expand Down Expand Up @@ -306,7 +307,7 @@ export class Inngest<
* happens, show a warning that this may not be intended, but don't throw.
*/
if (!payloads.length) {
return console.warn(
console.warn(
prettyError({
type: "warn",
whatHappened: "`inngest.send()` called with no events",
Expand All @@ -317,6 +318,8 @@ export class Inngest<
stack: true,
})
);

return [];
}

// When sending events, check if the dev server is available. If so, use the
Expand All @@ -339,11 +342,22 @@ export class Inngest<
headers: { ...this.headers },
});

if (response.status >= 200 && response.status < 300) {
return;
}
let rawBody: unknown;

try {
rawBody = await response.json();

throw await this.#getResponseError(response);
const body = await z
.object({
ids: z.array(z.string()),
status: z.number().min(200).max(299),
})
.parseAsync(rawBody);
Copy link
Member

Choose a reason for hiding this comment

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

If we decide at some point to return more data from the response and the shape of the response changes, would this parsing fail? Or will it only parse the data expected and ignore the rest?


return body.ids;
} catch (err) {
throw this.#getResponseError(response, rawBody);
}
}

public createFunction<
Expand Down
11 changes: 9 additions & 2 deletions src/components/InngestStepTools.test.ts
Expand Up @@ -244,8 +244,15 @@ describe("sleepUntil", () => {

describe("sendEvent", () => {
describe("runtime", () => {
const fetchMock = jest.fn(() =>
Promise.resolve({ status: 200 })
const fetchMock = jest.fn((input: unknown) =>
Promise.resolve({
status: 200,
json: () =>
Promise.resolve({
ids: Array.isArray(input) ? input.map((_, i) => `${i}`) : ["0"],
status: 200,
}),
})
) as unknown as typeof fetch;

const client = createClient({
Expand Down
8 changes: 4 additions & 4 deletions src/components/InngestStepTools.ts
Expand Up @@ -265,15 +265,15 @@ export const createStepTools = <
* Returns a promise that will resolve once the event has been sent.
*/
sendEvent: createTool<{
<Payload extends SendEventPayload<Events>>(
payload: Payload
): Promise<void>;
<Payload extends SendEventPayload<Events>>(payload: Payload): ReturnType<
Inngest["send"]
>;
<Event extends keyof Events & string>(
name: Event,
payload: SingleOrArray<
PartialK<Omit<Events[Event], "name" | "v">, "ts">
>
): Promise<void>;
): ReturnType<Inngest["send"]>;
}>(
(nameOrPayload, maybePayload) => {
let payloads: ValueOf<Events>[];
Expand Down