Skip to content

Commit 44311f5

Browse files
authored
refactor handler (calcom#25323)
1 parent 0af6354 commit 44311f5

4 files changed

Lines changed: 390 additions & 5 deletions

File tree

packages/app-store/utils.test.ts

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import { describe, it, expect } from "vitest";
2+
3+
import type { App } from "@calcom/types/App";
4+
import type { CredentialForCalendarService } from "@calcom/types/Credential";
5+
6+
import { sanitizeAppForViewer } from "./utils";
7+
import type { CredentialDataWithTeamName, LocationOption } from "./utils";
8+
9+
describe("sanitizeAppForViewer", () => {
10+
it("should remove key, credential, and credentials properties", () => {
11+
const mockCredential: CredentialDataWithTeamName = {
12+
id: 1,
13+
type: "daily_video",
14+
key: { api_key: "secret-api-key" },
15+
userId: 1,
16+
user: { email: "test@example.com" },
17+
teamId: null,
18+
appId: "daily-video",
19+
invalid: false,
20+
delegatedTo: null,
21+
delegatedToId: null,
22+
delegationCredentialId: null,
23+
team: null,
24+
};
25+
26+
const mockApp: App & {
27+
credential: CredentialDataWithTeamName | null;
28+
credentials: CredentialDataWithTeamName[];
29+
locationOption: LocationOption | null;
30+
} = {
31+
type: "daily_video",
32+
name: "Daily Video",
33+
description: "Video conferencing",
34+
variant: "conferencing",
35+
slug: "daily-video",
36+
categories: ["conferencing"],
37+
logo: "/logo.png",
38+
publisher: "Daily",
39+
url: "https://daily.co",
40+
email: "support@daily.co",
41+
key: { api_key: "secret-global-api-key" },
42+
credential: mockCredential,
43+
credentials: [mockCredential],
44+
locationOption: {
45+
value: "integrations:daily_video",
46+
label: "Daily Video",
47+
},
48+
};
49+
50+
const sanitized = sanitizeAppForViewer(mockApp);
51+
52+
// Should not have key, credential, or credentials
53+
expect(sanitized).not.toHaveProperty("key");
54+
expect(sanitized).not.toHaveProperty("credential");
55+
expect(sanitized).not.toHaveProperty("credentials");
56+
57+
// Should have all other properties
58+
expect(sanitized).toHaveProperty("type", "daily_video");
59+
expect(sanitized).toHaveProperty("name", "Daily Video");
60+
expect(sanitized).toHaveProperty("slug", "daily-video");
61+
expect(sanitized).toHaveProperty("locationOption");
62+
expect(sanitized.locationOption).toEqual({
63+
value: "integrations:daily_video",
64+
label: "Daily Video",
65+
});
66+
});
67+
68+
it("should handle apps without credential or credentials", () => {
69+
const mockApp: App & {
70+
credential?: CredentialDataWithTeamName | null;
71+
credentials?: CredentialDataWithTeamName[];
72+
locationOption?: LocationOption | null;
73+
} = {
74+
type: "zoom_video",
75+
name: "Zoom",
76+
description: "Video conferencing",
77+
variant: "conferencing",
78+
slug: "zoom",
79+
categories: ["conferencing"],
80+
logo: "/logo.png",
81+
publisher: "Zoom",
82+
url: "https://zoom.us",
83+
email: "support@zoom.us",
84+
key: { api_key: "secret-key" },
85+
};
86+
87+
const sanitized = sanitizeAppForViewer(mockApp);
88+
89+
expect(sanitized).not.toHaveProperty("key");
90+
expect(sanitized).not.toHaveProperty("credential");
91+
expect(sanitized).not.toHaveProperty("credentials");
92+
expect(sanitized).toHaveProperty("slug", "zoom");
93+
});
94+
95+
it("should preserve all non-sensitive properties", () => {
96+
const mockApp: App & {
97+
credential: CredentialDataWithTeamName | null;
98+
credentials: CredentialDataWithTeamName[];
99+
locationOption: LocationOption | null;
100+
} = {
101+
type: "stripe_payment",
102+
name: "Stripe",
103+
description: "Payment processing",
104+
variant: "payment",
105+
slug: "stripe",
106+
categories: ["payment"],
107+
logo: "/logo.png",
108+
publisher: "Stripe",
109+
url: "https://stripe.com",
110+
email: "support@stripe.com",
111+
verified: true,
112+
trending: true,
113+
rating: 4.5,
114+
reviews: 1000,
115+
isGlobal: false,
116+
key: { api_key: "sk_live_secret" },
117+
credential: null,
118+
credentials: [],
119+
locationOption: null,
120+
appData: {
121+
location: {
122+
type: "integrations:stripe",
123+
label: "Stripe",
124+
linkType: "dynamic",
125+
},
126+
},
127+
};
128+
129+
const sanitized = sanitizeAppForViewer(mockApp);
130+
131+
expect(sanitized).not.toHaveProperty("key");
132+
expect(sanitized).not.toHaveProperty("credential");
133+
expect(sanitized).not.toHaveProperty("credentials");
134+
expect(sanitized.verified).toBe(true);
135+
expect(sanitized.trending).toBe(true);
136+
expect(sanitized.rating).toBe(4.5);
137+
expect(sanitized.reviews).toBe(1000);
138+
expect(sanitized.appData).toBeDefined();
139+
});
140+
});

packages/app-store/utils.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ function getApps(credentials: CredentialDataWithTeamName[], filterOnCredentials?
5757
const credential = {
5858
id: 0,
5959
type: appMeta.type,
60-
60+
6161
key: appMeta.key!,
6262
userId: 0,
6363
user: { email: "" },
@@ -174,4 +174,15 @@ export const defaultVideoAppCategories: AppCategories[] = [
174174
"video",
175175
];
176176

177+
export function sanitizeAppForViewer<
178+
T extends App & {
179+
credential?: CredentialDataWithTeamName | null;
180+
credentials?: CredentialDataWithTeamName[];
181+
locationOption?: LocationOption | null;
182+
}
183+
>(app: T): Omit<T, "key" | "credential" | "credentials"> {
184+
const { key: _, credential: _1, credentials: _2, ...sanitizedApp } = app;
185+
return sanitizedApp;
186+
}
187+
177188
export default getApps;

0 commit comments

Comments
 (0)