Skip to content

Commit 7e39a2f

Browse files
authored
Merge branch 'main' into devin/rename-repository-files-1753364183
2 parents 162eb3a + 6c93647 commit 7e39a2f

File tree

241 files changed

+12358
-2583
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

241 files changed

+12358
-2583
lines changed

.cursor/rules/review.mdc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Review Instructions
22

3+
## File Naming Conventions
4+
5+
- **Repository Files**: Must include `Repository` suffix, prefix with technology if applicable (e.g., `PrismaAppRepository.ts`), use PascalCase matching exported class
6+
- **Service Files**: Must include `Service` suffix, use PascalCase matching exported class, avoid generic names (e.g., `MembershipService.ts`)
7+
- **New files**: Avoid dot-suffixes like `.service.ts` or `.repository.ts`; reserve `.test.ts`, `.spec.ts`, `.types.ts` for their specific purposes
8+
9+
## Code Quality
10+
311
- Prefer early returns. It is recommended to throw/return early so we can ensure null-checks and prevent further nesting.
412
- Prefer Composition over Prop Drilling. Instead of relying on prop drilling, let's try to take advantage of react children feature.
513
- For Prisma queries:

.github/workflows/changesets.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,7 @@ jobs:
3434
env:
3535
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3636
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
37-
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
37+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
38+
VITE_BOOKER_EMBED_OAUTH_CLIENT_ID: ${{ secrets.VITE_BOOKER_EMBED_OAUTH_CLIENT_ID }}
39+
VITE_BOOKER_EMBED_API_URL: ${{ secrets.VITE_BOOKER_EMBED_API_URL }}
40+
NEXT_PUBLIC_WEBAPP_URL: ${{ secrets.NEXT_PUBLIC_WEBAPP_URL }}

.yarn/versions/178162d8.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
undecided:
2+
- calcom-monorepo
3+
- "@calcom/prisma"

.yarn/versions/26346cfc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
undecided:
2+
- "@calcom/prisma"

CONTRIBUTING.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,57 @@ Write with the future in mind. If there are trade-offs, edge cases, or temporary
9595
</tr>
9696
</table>
9797

98+
## File Naming Conventions
99+
100+
To ensure consistency and make files easy to fuzzy-find, we follow the naming conventions below for **services**, **repositories**, and other class-based files.
101+
102+
### Repository Files
103+
104+
- Repository class files must include the `Repository` suffix.
105+
- If the repository is backed by a specific technology (e.g. Prisma), prefix the filename and class name with it.
106+
- File name must match the exported class exactly (PascalCase).
107+
108+
**Pattern:**
109+
110+
`Prisma<Entity>Repository.ts`
111+
112+
**Examples:**
113+
114+
```ts
115+
// File: PrismaAppRepository.ts
116+
export class PrismaAppRepository { ... }
117+
118+
// File: PrismaMembershipRepository.ts
119+
export class PrismaMembershipRepository { ... }
120+
```
121+
122+
This avoids ambiguous filenames like app.ts and improves discoverability in editors.
123+
124+
### Service Files
125+
126+
- Service class files must include the Service suffix.
127+
- File name should be in PascalCase, matching the exported class.
128+
- Keep naming specific — avoid generic names like AppService.ts.
129+
130+
**Pattern:**
131+
132+
`<Entity>Service.ts`
133+
134+
**Examples:**
135+
136+
```ts
137+
// File: MembershipService.ts
138+
export class MembershipService { ... }
139+
140+
// File: HashedLinkService.ts
141+
export class HashedLinkService { ... }
142+
```
143+
144+
**Note:**
145+
146+
- New files must avoid dot-suffixes like .service.ts or .repository.ts; these will be migrated from the existing codebase progressively.
147+
- We still reserve suffixes such as .test.ts, .spec.ts, and .types.ts for their respective use cases.
148+
98149
## Developing
99150

100151
[See README](https://github.com/calcom/cal.com#development)

__checks__/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
# Checkly Tests
22

33
Run as `yarn checkly test`
4+
Optionally, run as `yarn checkly test --record` to be able to get detailed debugging view in Checkly UI
5+
Run the tests for a particylar file as `yarn checkly test {filePattern}`
46
Deploy the tests as `yarn checkly deploy`

__checks__/csp-login.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { test, expect } from "@playwright/test";
2+
3+
test.describe("CSP Headers", () => {
4+
test("Login page should have CSP header with nonce", async ({ page }) => {
5+
const targetUrl = process.env.ENVIRONMENT_URL || "https://app.cal.com";
6+
7+
const response = await page.goto(`${targetUrl}/auth/login`);
8+
9+
expect(response?.status()).toBe(200);
10+
11+
const cspHeader = response?.headers()["content-security-policy"];
12+
expect(cspHeader).toBeTruthy();
13+
14+
// Verify nonce is present in CSP header
15+
const nonceMatch = cspHeader?.match(/'nonce-([^']+)'/);
16+
expect(nonceMatch).toBeTruthy();
17+
expect(nonceMatch![1]).toHaveLength(24);
18+
expect(nonceMatch![1]).toMatch(/==$/);
19+
20+
await page.screenshot({ path: "screenshot.jpg" });
21+
});
22+
});

apps/api/v1/lib/helpers/verifyApiKey.test.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,29 @@ import { MembershipRole, UserPermissionRole } from "@calcom/prisma/enums";
1414

1515
import { verifyApiKey } from "./verifyApiKey";
1616

17+
vi.mock("@calcom/lib/crypto", () => ({
18+
symmetricDecrypt: vi.fn().mockReturnValue("mocked-decrypted-value"),
19+
symmetricEncrypt: vi.fn().mockReturnValue("mocked-encrypted-value"),
20+
}));
21+
1722
type CustomNextApiRequest = NextApiRequest & Request;
1823
type CustomNextApiResponse = NextApiResponse & Response;
1924

25+
beforeEach(() => {
26+
vi.stubEnv("CALENDSO_ENCRYPTION_KEY", "22gfxhWUlcKliUeXcu8xNah2+HP/29ZX");
27+
});
28+
2029
afterEach(() => {
2130
vi.resetAllMocks();
31+
vi.unstubAllEnvs();
2232
});
2333

2434
const mockDeploymentRepository: IDeploymentRepository = {
2535
getLicenseKeyWithId: vi.fn().mockResolvedValue("mockLicenseKey"), // Mocked return value
2636
getSignatureToken: vi.fn().mockResolvedValue("mockSignatureToken"),
2737
};
2838

29-
// TODO: Fix the skip condition for this test suite
30-
describe.skip("Verify API key", () => {
39+
describe("Verify API key", () => {
3140
let service: ILicenseKeyService;
3241

3342
beforeEach(async () => {

apps/api/v1/pages/api/bookings/[id]/_get.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import type { NextApiRequest } from "next";
22

3+
import { ErrorCode } from "@calcom/lib/errorCodes";
4+
import { ErrorWithCode } from "@calcom/lib/errors";
35
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
46
import prisma from "@calcom/prisma";
57

@@ -106,6 +108,11 @@ export async function getHandler(req: NextApiRequest) {
106108
eventType: expand.includes("team") ? { include: { team: true } } : false,
107109
},
108110
});
111+
112+
if (!booking) {
113+
throw new ErrorWithCode(ErrorCode.BookingNotFound, "Booking not found");
114+
}
115+
109116
return { booking: schemaBookingReadPublic.parse(booking) };
110117
}
111118

apps/api/v1/pages/api/bookings/_post.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,14 @@ async function handler(req: NextApiRequest) {
254254
throw new HttpError({ statusCode: 400, message: knownError.message });
255255
}
256256

257+
if (knownError?.message === ErrorCode.RequestBodyInvalid) {
258+
throw new HttpError({ statusCode: 400, message: knownError.message });
259+
}
260+
261+
if (knownError?.message === ErrorCode.EventTypeNotFound) {
262+
throw new HttpError({ statusCode: 400, message: knownError.message });
263+
}
264+
257265
throw error;
258266
}
259267
}

0 commit comments

Comments
 (0)