Skip to content

Commit 85517f8

Browse files
committed
cloud: isolate managed google route deps
1 parent f5477f4 commit 85517f8

9 files changed

Lines changed: 73 additions & 94 deletions

File tree

app/api/v1/milady/google/calendar/events/route.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import type { NextRequest } from "next/server";
22
import { NextResponse } from "next/server";
33
import { z } from "zod";
4-
import { requireAuthOrApiKeyWithOrg } from "@/lib/auth";
5-
import {
6-
createManagedGoogleCalendarEvent,
7-
MiladyGoogleConnectorError,
8-
} from "@/lib/services/milady-google-connector";
4+
import { miladyGoogleRouteDeps } from "@/lib/services/milady-google-route-deps";
95

106
export const dynamic = "force-dynamic";
117
export const maxDuration = 30;
@@ -30,7 +26,7 @@ const requestSchema = z.object({
3026

3127
export async function POST(request: NextRequest) {
3228
try {
33-
const { user } = await requireAuthOrApiKeyWithOrg(request);
29+
const { user } = await miladyGoogleRouteDeps.requireAuthOrApiKeyWithOrg(request);
3430
const parsed = requestSchema.safeParse(await request.json());
3531
if (!parsed.success) {
3632
return NextResponse.json(
@@ -40,7 +36,7 @@ export async function POST(request: NextRequest) {
4036
}
4137

4238
return NextResponse.json(
43-
await createManagedGoogleCalendarEvent({
39+
await miladyGoogleRouteDeps.createManagedGoogleCalendarEvent({
4440
organizationId: user.organization_id,
4541
userId: user.id,
4642
side: parsed.data.side ?? "owner",
@@ -56,7 +52,7 @@ export async function POST(request: NextRequest) {
5652
{ status: 201 },
5753
);
5854
} catch (error) {
59-
if (error instanceof MiladyGoogleConnectorError) {
55+
if (error instanceof miladyGoogleRouteDeps.MiladyGoogleConnectorError) {
6056
return NextResponse.json({ error: error.message }, { status: error.status });
6157
}
6258
return NextResponse.json(

app/api/v1/milady/google/calendar/feed/route.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
import type { NextRequest } from "next/server";
22
import { NextResponse } from "next/server";
3-
import { requireAuthOrApiKeyWithOrg } from "@/lib/auth";
4-
import {
5-
fetchManagedGoogleCalendarFeed,
6-
MiladyGoogleConnectorError,
7-
} from "@/lib/services/milady-google-connector";
3+
import { miladyGoogleRouteDeps } from "@/lib/services/milady-google-route-deps";
84

95
export const dynamic = "force-dynamic";
106
export const maxDuration = 30;
117

128
export async function GET(request: NextRequest) {
139
try {
14-
const { user } = await requireAuthOrApiKeyWithOrg(request);
10+
const { user } = await miladyGoogleRouteDeps.requireAuthOrApiKeyWithOrg(request);
1511
const searchParams = request.nextUrl.searchParams;
1612
const rawSide = searchParams.get("side");
1713
const calendarId = searchParams.get("calendarId")?.trim() || "primary";
@@ -27,7 +23,7 @@ export async function GET(request: NextRequest) {
2723
}
2824

2925
return NextResponse.json(
30-
await fetchManagedGoogleCalendarFeed({
26+
await miladyGoogleRouteDeps.fetchManagedGoogleCalendarFeed({
3127
organizationId: user.organization_id,
3228
userId: user.id,
3329
side: rawSide === "agent" ? "agent" : "owner",
@@ -38,7 +34,7 @@ export async function GET(request: NextRequest) {
3834
}),
3935
);
4036
} catch (error) {
41-
if (error instanceof MiladyGoogleConnectorError) {
37+
if (error instanceof miladyGoogleRouteDeps.MiladyGoogleConnectorError) {
4238
return NextResponse.json({ error: error.message }, { status: error.status });
4339
}
4440
return NextResponse.json(

app/api/v1/milady/google/connect/initiate/route.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import type { NextRequest } from "next/server";
22
import { NextResponse } from "next/server";
33
import { z } from "zod";
4-
import { requireAuthOrApiKeyWithOrg } from "@/lib/auth";
5-
import {
6-
initiateManagedGoogleConnection,
7-
MiladyGoogleConnectorError,
8-
} from "@/lib/services/milady-google-connector";
4+
import { miladyGoogleRouteDeps } from "@/lib/services/milady-google-route-deps";
95

106
export const dynamic = "force-dynamic";
117
export const maxDuration = 30;
@@ -28,7 +24,7 @@ const requestSchema = z.object({
2824

2925
export async function POST(request: NextRequest) {
3026
try {
31-
const { user } = await requireAuthOrApiKeyWithOrg(request);
27+
const { user } = await miladyGoogleRouteDeps.requireAuthOrApiKeyWithOrg(request);
3228
const parsed = requestSchema.safeParse(await request.json().catch(() => ({})));
3329
if (!parsed.success) {
3430
return NextResponse.json(
@@ -37,7 +33,7 @@ export async function POST(request: NextRequest) {
3733
);
3834
}
3935
return NextResponse.json(
40-
await initiateManagedGoogleConnection({
36+
await miladyGoogleRouteDeps.initiateManagedGoogleConnection({
4137
organizationId: user.organization_id,
4238
userId: user.id,
4339
side: parsed.data.side ?? "owner",
@@ -46,7 +42,7 @@ export async function POST(request: NextRequest) {
4642
}),
4743
);
4844
} catch (error) {
49-
if (error instanceof MiladyGoogleConnectorError) {
45+
if (error instanceof miladyGoogleRouteDeps.MiladyGoogleConnectorError) {
5046
return NextResponse.json({ error: error.message }, { status: error.status });
5147
}
5248
return NextResponse.json(

app/api/v1/milady/google/disconnect/route.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import type { NextRequest } from "next/server";
22
import { NextResponse } from "next/server";
33
import { z } from "zod";
4-
import { requireAuthOrApiKeyWithOrg } from "@/lib/auth";
5-
import {
6-
disconnectManagedGoogleConnection,
7-
MiladyGoogleConnectorError,
8-
} from "@/lib/services/milady-google-connector";
4+
import { miladyGoogleRouteDeps } from "@/lib/services/milady-google-route-deps";
95

106
export const dynamic = "force-dynamic";
117
export const maxDuration = 30;
@@ -17,7 +13,7 @@ const requestSchema = z.object({
1713

1814
export async function POST(request: NextRequest) {
1915
try {
20-
const { user } = await requireAuthOrApiKeyWithOrg(request);
16+
const { user } = await miladyGoogleRouteDeps.requireAuthOrApiKeyWithOrg(request);
2117
const parsed = requestSchema.safeParse(await request.json().catch(() => ({})));
2218
if (!parsed.success) {
2319
return NextResponse.json(
@@ -26,15 +22,15 @@ export async function POST(request: NextRequest) {
2622
);
2723
}
2824

29-
await disconnectManagedGoogleConnection({
25+
await miladyGoogleRouteDeps.disconnectManagedGoogleConnection({
3026
organizationId: user.organization_id,
3127
userId: user.id,
3228
side: parsed.data.side ?? "owner",
3329
connectionId: parsed.data.connectionId ?? null,
3430
});
3531
return NextResponse.json({ ok: true });
3632
} catch (error) {
37-
if (error instanceof MiladyGoogleConnectorError) {
33+
if (error instanceof miladyGoogleRouteDeps.MiladyGoogleConnectorError) {
3834
return NextResponse.json({ error: error.message }, { status: error.status });
3935
}
4036
return NextResponse.json(

app/api/v1/milady/google/gmail/reply-send/route.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import type { NextRequest } from "next/server";
22
import { NextResponse } from "next/server";
33
import { z } from "zod";
4-
import { requireAuthOrApiKeyWithOrg } from "@/lib/auth";
5-
import {
6-
MiladyGoogleConnectorError,
7-
sendManagedGoogleReply,
8-
} from "@/lib/services/milady-google-connector";
4+
import { miladyGoogleRouteDeps } from "@/lib/services/milady-google-route-deps";
95

106
export const dynamic = "force-dynamic";
117
export const maxDuration = 30;
@@ -22,7 +18,7 @@ const requestSchema = z.object({
2218

2319
export async function POST(request: NextRequest) {
2420
try {
25-
const { user } = await requireAuthOrApiKeyWithOrg(request);
21+
const { user } = await miladyGoogleRouteDeps.requireAuthOrApiKeyWithOrg(request);
2622
const parsed = requestSchema.safeParse(await request.json());
2723
if (!parsed.success) {
2824
return NextResponse.json(
@@ -31,7 +27,7 @@ export async function POST(request: NextRequest) {
3127
);
3228
}
3329

34-
await sendManagedGoogleReply({
30+
await miladyGoogleRouteDeps.sendManagedGoogleReply({
3531
organizationId: user.organization_id,
3632
userId: user.id,
3733
side: parsed.data.side ?? "owner",
@@ -44,7 +40,7 @@ export async function POST(request: NextRequest) {
4440
});
4541
return NextResponse.json({ ok: true });
4642
} catch (error) {
47-
if (error instanceof MiladyGoogleConnectorError) {
43+
if (error instanceof miladyGoogleRouteDeps.MiladyGoogleConnectorError) {
4844
return NextResponse.json({ error: error.message }, { status: error.status });
4945
}
5046
return NextResponse.json(

app/api/v1/milady/google/gmail/triage/route.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
import type { NextRequest } from "next/server";
22
import { NextResponse } from "next/server";
3-
import { requireAuthOrApiKeyWithOrg } from "@/lib/auth";
4-
import {
5-
fetchManagedGoogleGmailTriage,
6-
MiladyGoogleConnectorError,
7-
} from "@/lib/services/milady-google-connector";
3+
import { miladyGoogleRouteDeps } from "@/lib/services/milady-google-route-deps";
84

95
export const dynamic = "force-dynamic";
106
export const maxDuration = 30;
117

128
export async function GET(request: NextRequest) {
139
try {
14-
const { user } = await requireAuthOrApiKeyWithOrg(request);
10+
const { user } = await miladyGoogleRouteDeps.requireAuthOrApiKeyWithOrg(request);
1511
const rawSide = request.nextUrl.searchParams.get("side");
1612
const rawMaxResults = request.nextUrl.searchParams.get("maxResults");
1713
if (rawSide !== null && rawSide !== "owner" && rawSide !== "agent") {
@@ -27,15 +23,15 @@ export async function GET(request: NextRequest) {
2723
}
2824

2925
return NextResponse.json(
30-
await fetchManagedGoogleGmailTriage({
26+
await miladyGoogleRouteDeps.fetchManagedGoogleGmailTriage({
3127
organizationId: user.organization_id,
3228
userId: user.id,
3329
side: rawSide === "agent" ? "agent" : "owner",
3430
maxResults,
3531
}),
3632
);
3733
} catch (error) {
38-
if (error instanceof MiladyGoogleConnectorError) {
34+
if (error instanceof miladyGoogleRouteDeps.MiladyGoogleConnectorError) {
3935
return NextResponse.json({ error: error.message }, { status: error.status });
4036
}
4137
return NextResponse.json(

app/api/v1/milady/google/status/route.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,26 @@
11
import type { NextRequest } from "next/server";
22
import { NextResponse } from "next/server";
3-
import { requireAuthOrApiKeyWithOrg } from "@/lib/auth";
4-
import {
5-
getManagedGoogleConnectorStatus,
6-
MiladyGoogleConnectorError,
7-
} from "@/lib/services/milady-google-connector";
3+
import { miladyGoogleRouteDeps } from "@/lib/services/milady-google-route-deps";
84

95
export const dynamic = "force-dynamic";
106
export const maxDuration = 30;
117

128
export async function GET(request: NextRequest) {
139
try {
14-
const { user } = await requireAuthOrApiKeyWithOrg(request);
10+
const { user } = await miladyGoogleRouteDeps.requireAuthOrApiKeyWithOrg(request);
1511
const rawSide = request.nextUrl.searchParams.get("side");
1612
if (rawSide !== null && rawSide !== "owner" && rawSide !== "agent") {
1713
return NextResponse.json({ error: "side must be owner or agent." }, { status: 400 });
1814
}
1915
return NextResponse.json(
20-
await getManagedGoogleConnectorStatus({
16+
await miladyGoogleRouteDeps.getManagedGoogleConnectorStatus({
2117
organizationId: user.organization_id,
2218
userId: user.id,
2319
side: rawSide === "agent" ? "agent" : "owner",
2420
}),
2521
);
2622
} catch (error) {
27-
if (error instanceof MiladyGoogleConnectorError) {
23+
if (error instanceof miladyGoogleRouteDeps.MiladyGoogleConnectorError) {
2824
return NextResponse.json({ error: error.message }, { status: error.status });
2925
}
3026
return NextResponse.json(
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { requireAuthOrApiKeyWithOrg } from "@/lib/auth";
2+
import {
3+
createManagedGoogleCalendarEvent,
4+
disconnectManagedGoogleConnection,
5+
fetchManagedGoogleCalendarFeed,
6+
fetchManagedGoogleGmailTriage,
7+
getManagedGoogleConnectorStatus,
8+
initiateManagedGoogleConnection,
9+
MiladyGoogleConnectorError,
10+
sendManagedGoogleReply,
11+
} from "@/lib/services/milady-google-connector";
12+
13+
export const miladyGoogleRouteDeps = {
14+
requireAuthOrApiKeyWithOrg,
15+
getManagedGoogleConnectorStatus,
16+
initiateManagedGoogleConnection,
17+
disconnectManagedGoogleConnection,
18+
fetchManagedGoogleCalendarFeed,
19+
createManagedGoogleCalendarEvent,
20+
fetchManagedGoogleGmailTriage,
21+
sendManagedGoogleReply,
22+
MiladyGoogleConnectorError,
23+
};

packages/tests/unit/milady-google-routes.test.ts

Lines changed: 22 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
1-
import { beforeEach, describe, expect, mock, test } from "bun:test";
1+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
22
import { NextRequest } from "next/server";
3+
import { POST as postCalendarEvent } from "@/app/api/v1/milady/google/calendar/events/route";
4+
import { GET as getCalendarFeed } from "@/app/api/v1/milady/google/calendar/feed/route";
5+
import { POST as postConnectInitiate } from "@/app/api/v1/milady/google/connect/initiate/route";
6+
import { POST as postDisconnect } from "@/app/api/v1/milady/google/disconnect/route";
7+
import { POST as postReplySend } from "@/app/api/v1/milady/google/gmail/reply-send/route";
8+
import { GET as getGmailTriage } from "@/app/api/v1/milady/google/gmail/triage/route";
9+
import { GET as getStatus } from "@/app/api/v1/milady/google/status/route";
10+
import { miladyGoogleRouteDeps } from "../../lib/services/milady-google-route-deps";
311
import { jsonRequest } from "./api/route-test-helpers";
412

513
const mockRequireAuthOrApiKeyWithOrg = mock();
@@ -10,46 +18,18 @@ const mockFetchCalendarFeed = mock();
1018
const mockCreateCalendarEvent = mock();
1119
const mockFetchGmailTriage = mock();
1220
const mockSendReply = mock();
13-
14-
mock.module("@/lib/auth", () => ({
15-
requireAuthOrApiKeyWithOrg: mockRequireAuthOrApiKeyWithOrg,
16-
}));
17-
18-
mock.module("@/lib/services/milady-google-connector", () => ({
19-
getManagedGoogleConnectorStatus: mockGetStatus,
20-
initiateManagedGoogleConnection: mockInitiateConnection,
21-
disconnectManagedGoogleConnection: mockDisconnectConnection,
22-
fetchManagedGoogleCalendarFeed: mockFetchCalendarFeed,
23-
createManagedGoogleCalendarEvent: mockCreateCalendarEvent,
24-
fetchManagedGoogleGmailTriage: mockFetchGmailTriage,
25-
sendManagedGoogleReply: mockSendReply,
26-
MiladyGoogleConnectorError: class MiladyGoogleConnectorError extends Error {
27-
constructor(
28-
public readonly status: number,
29-
message: string,
30-
) {
31-
super(message);
32-
this.name = "MiladyGoogleConnectorError";
33-
}
34-
},
35-
}));
36-
37-
const { POST: postCalendarEvent } = await import(
38-
"@/app/api/v1/milady/google/calendar/events/route"
39-
);
40-
const { GET: getCalendarFeed } = await import("@/app/api/v1/milady/google/calendar/feed/route");
41-
const { POST: postConnectInitiate } = await import(
42-
"@/app/api/v1/milady/google/connect/initiate/route"
43-
);
44-
const { POST: postDisconnect } = await import("@/app/api/v1/milady/google/disconnect/route");
45-
const { POST: postReplySend } = await import("@/app/api/v1/milady/google/gmail/reply-send/route");
46-
const { GET: getGmailTriage } = await import("@/app/api/v1/milady/google/gmail/triage/route");
47-
const { GET: getStatus } = await import("@/app/api/v1/milady/google/status/route");
48-
49-
mock.restore();
21+
const originalRouteDeps = { ...miladyGoogleRouteDeps };
5022

5123
describe("Milady managed Google routes", () => {
5224
beforeEach(() => {
25+
miladyGoogleRouteDeps.requireAuthOrApiKeyWithOrg = mockRequireAuthOrApiKeyWithOrg;
26+
miladyGoogleRouteDeps.getManagedGoogleConnectorStatus = mockGetStatus;
27+
miladyGoogleRouteDeps.initiateManagedGoogleConnection = mockInitiateConnection;
28+
miladyGoogleRouteDeps.disconnectManagedGoogleConnection = mockDisconnectConnection;
29+
miladyGoogleRouteDeps.fetchManagedGoogleCalendarFeed = mockFetchCalendarFeed;
30+
miladyGoogleRouteDeps.createManagedGoogleCalendarEvent = mockCreateCalendarEvent;
31+
miladyGoogleRouteDeps.fetchManagedGoogleGmailTriage = mockFetchGmailTriage;
32+
miladyGoogleRouteDeps.sendManagedGoogleReply = mockSendReply;
5333
mockRequireAuthOrApiKeyWithOrg.mockReset();
5434
mockGetStatus.mockReset();
5535
mockInitiateConnection.mockReset();
@@ -67,6 +47,10 @@ describe("Milady managed Google routes", () => {
6747
});
6848
});
6949

50+
afterEach(() => {
51+
Object.assign(miladyGoogleRouteDeps, originalRouteDeps);
52+
});
53+
7054
test("GET /api/v1/milady/google/status returns the managed connector status", async () => {
7155
mockGetStatus.mockResolvedValue({
7256
provider: "google",

0 commit comments

Comments
 (0)