Fix type name clashing for eslint parser (#10491)
parent
17855251ff
commit
7c64b6a6e2
|
@ -1,23 +1,22 @@
|
|||
import Link from "next/link";
|
||||
import type { IframeHTMLAttributes } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { CAL_URL } from "@calcom/lib/constants";
|
||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
||||
import React, { useState } from "react";
|
||||
|
||||
import useAddAppMutation from "@calcom/app-store/_utils/useAddAppMutation";
|
||||
import { InstallAppButton, AppDependencyComponent } from "@calcom/app-store/components";
|
||||
import DisconnectIntegration from "@calcom/features/apps/components/DisconnectIntegration";
|
||||
import { Spinner } from "@calcom/features/calendars/weeklyview/components/spinner/Spinner";
|
||||
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
|
||||
import type { UserAdminTeams } from "@calcom/features/ee/teams/lib/getUserAdminTeams";
|
||||
import Shell from "@calcom/features/shell/Shell";
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
import { CAL_URL } from "@calcom/lib/constants";
|
||||
import { APP_NAME, COMPANY_NAME, SUPPORT_MAIL_ADDRESS } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import type { App as AppType } from "@calcom/types/App";
|
||||
import type { App as AppType, AppFrontendPayload } from "@calcom/types/App";
|
||||
import type { ButtonProps } from "@calcom/ui";
|
||||
import type { AppFrontendPayload as App } from "@calcom/types/App";
|
||||
import { Button, showToast, SkeletonButton, SkeletonText, HeadSeo, Badge } from "@calcom/ui";
|
||||
import {
|
||||
Dropdown,
|
||||
|
@ -28,9 +27,7 @@ import {
|
|||
DropdownItem,
|
||||
Avatar,
|
||||
} from "@calcom/ui";
|
||||
import { BookOpen, Check, ExternalLink, File, Flag, Mail, Plus, Shield } from "@calcom/ui/components/icon";
|
||||
import { Spinner } from "@calcom/features/calendars/weeklyview/components/spinner/Spinner";
|
||||
|
||||
import { BookOpen, Check, ExternalLink, File, Flag, Mail, Shield } from "@calcom/ui/components/icon";
|
||||
|
||||
/* These app slugs all require Google Cal to be installed */
|
||||
|
||||
|
@ -82,9 +79,11 @@ const Component = ({
|
|||
{ appType: type },
|
||||
{
|
||||
onSettled(data) {
|
||||
const credentialsCount = data?.credentials.length || 0
|
||||
setShowDisconnectIntegration(data?.userAdminTeams.length ? credentialsCount >= data?.userAdminTeams.length : credentialsCount > 0);
|
||||
setExistingCredentials(data?.credentials.map(credential => credential.id) || []);
|
||||
const credentialsCount = data?.credentials.length || 0;
|
||||
setShowDisconnectIntegration(
|
||||
data?.userAdminTeams.length ? credentialsCount >= data?.userAdminTeams.length : credentialsCount > 0
|
||||
);
|
||||
setExistingCredentials(data?.credentials.map((credential) => credential.id) || []);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
@ -186,7 +185,13 @@ const Component = ({
|
|||
};
|
||||
}
|
||||
return (
|
||||
<InstallAppButtonChild appCategories={categories} userAdminTeams={appDbQuery.data?.userAdminTeams} addAppMutationInput={{ type, variant, slug }} multiInstall {...props} />
|
||||
<InstallAppButtonChild
|
||||
appCategories={categories}
|
||||
userAdminTeams={appDbQuery.data?.userAdminTeams}
|
||||
addAppMutationInput={{ type, variant, slug }}
|
||||
multiInstall
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
@ -217,7 +222,13 @@ const Component = ({
|
|||
};
|
||||
}
|
||||
return (
|
||||
<InstallAppButtonChild appCategories={categories} userAdminTeams={appDbQuery.data?.userAdminTeams} addAppMutationInput={{ type, variant, slug }} credentials={appDbQuery.data?.credentials} {...props} />
|
||||
<InstallAppButtonChild
|
||||
appCategories={categories}
|
||||
userAdminTeams={appDbQuery.data?.userAdminTeams}
|
||||
addAppMutationInput={{ type, variant, slug }}
|
||||
credentials={appDbQuery.data?.credentials}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
@ -398,13 +409,12 @@ const InstallAppButtonChild = ({
|
|||
...props
|
||||
}: {
|
||||
userAdminTeams?: UserAdminTeams;
|
||||
addAppMutationInput: { type: App["type"]; variant: string; slug: string };
|
||||
addAppMutationInput: { type: AppFrontendPayload["type"]; variant: string; slug: string };
|
||||
appCategories: string[];
|
||||
multiInstall?: boolean;
|
||||
credentials?: RouterOutputs["viewer"]["appCredentialsByType"]["credentials"];
|
||||
} & ButtonProps) => {
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
|
||||
const mutation = useAddAppMutation(null, {
|
||||
onSuccess: (data) => {
|
||||
|
@ -416,17 +426,21 @@ const InstallAppButtonChild = ({
|
|||
},
|
||||
});
|
||||
|
||||
if (!userAdminTeams?.length || appCategories.some((category) => ["calendar", "conferencing"].includes(category))) {
|
||||
return <Button
|
||||
data-testid="install-app-button"
|
||||
{...props}
|
||||
// @TODO: Overriding color and size prevent us from
|
||||
// having to duplicate InstallAppButton for now.
|
||||
color="primary"
|
||||
size="base">
|
||||
{multiInstall ? t("install_another") : t("install_app")}
|
||||
</Button>
|
||||
|
||||
if (
|
||||
!userAdminTeams?.length ||
|
||||
appCategories.some((category) => ["calendar", "conferencing"].includes(category))
|
||||
) {
|
||||
return (
|
||||
<Button
|
||||
data-testid="install-app-button"
|
||||
{...props}
|
||||
// @TODO: Overriding color and size prevent us from
|
||||
// having to duplicate InstallAppButton for now.
|
||||
color="primary"
|
||||
size="base">
|
||||
{multiInstall ? t("install_another") : t("install_app")}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -446,8 +460,7 @@ const InstallAppButtonChild = ({
|
|||
<DropdownMenuContent
|
||||
onInteractOutside={(event) => {
|
||||
if (mutation.isLoading) event.preventDefault();
|
||||
}}
|
||||
>
|
||||
}}>
|
||||
{mutation.isLoading && (
|
||||
<div className="z-1 fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
|
||||
<Spinner />
|
||||
|
@ -455,20 +468,18 @@ const InstallAppButtonChild = ({
|
|||
)}
|
||||
<DropdownMenuLabel>{t("install_app_on")}</DropdownMenuLabel>
|
||||
{userAdminTeams.map((team) => {
|
||||
|
||||
const isInstalled = credentials &&
|
||||
const isInstalled =
|
||||
credentials &&
|
||||
credentials.some((credential) =>
|
||||
credential?.teamId ? credential?.teamId === team.id : credential.userId === team.id
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<DropdownItem
|
||||
type="button"
|
||||
data-testid={team.isUser ? "install-app-button-personal" : "anything else"}
|
||||
key={team.id}
|
||||
disabled={
|
||||
isInstalled
|
||||
}
|
||||
disabled={isInstalled}
|
||||
StartIcon={(props) => (
|
||||
<Avatar
|
||||
alt={team.logo || ""}
|
||||
|
@ -482,11 +493,11 @@ const InstallAppButtonChild = ({
|
|||
team.isUser ? addAppMutationInput : { ...addAppMutationInput, teamId: team.id }
|
||||
);
|
||||
}}>
|
||||
<p>{team.name}{" "}
|
||||
{isInstalled &&
|
||||
`(${t("installed")})`}</p>
|
||||
<p>
|
||||
{team.name} {isInstalled && `(${t("installed")})`}
|
||||
</p>
|
||||
</DropdownItem>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenuPortal>
|
||||
|
|
|
@ -47,7 +47,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
// set expiry date as offset from current time.
|
||||
hubspotToken.expiryDate = Math.round(Date.now() + hubspotToken.expiresIn * 1000);
|
||||
|
||||
await createOAuthAppCredential({ appId: "hubspot", type: "hubspot_other_calendar" }, hubspotToken as any, req);
|
||||
await createOAuthAppCredential(
|
||||
{ appId: "hubspot", type: "hubspot_other_calendar" },
|
||||
hubspotToken as any,
|
||||
req
|
||||
);
|
||||
|
||||
const state = decodeOAuthState(req);
|
||||
res.redirect(
|
||||
|
|
|
@ -1,11 +1,22 @@
|
|||
import { describe, it, vi, expect } from "vitest";
|
||||
import { isOrganisationAdmin } from "@calcom/lib/server/queries/organisations";
|
||||
import { checkInputEmailIsValid, checkPermissions, getEmailsToInvite, getIsOrgVerified, getOrgConnectionInfo, throwIfInviteIsToOrgAndUserExists,createAndAutoJoinIfInOrg } from "./utils";
|
||||
import { MembershipRole } from "@calcom/prisma/enums";
|
||||
|
||||
import { isTeamAdmin } from "@calcom/lib/server/queries";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import type { TeamWithParent } from "./types";
|
||||
import { isOrganisationAdmin } from "@calcom/lib/server/queries/organisations";
|
||||
import type { User } from "@calcom/prisma/client";
|
||||
import { MembershipRole } from "@calcom/prisma/enums";
|
||||
|
||||
import { TRPCError } from "@trpc/server";
|
||||
|
||||
import type { TeamWithParent } from "./types";
|
||||
import {
|
||||
checkInputEmailIsValid,
|
||||
checkPermissions,
|
||||
getEmailsToInvite,
|
||||
getIsOrgVerified,
|
||||
getOrgConnectionInfo,
|
||||
throwIfInviteIsToOrgAndUserExists,
|
||||
createAndAutoJoinIfInOrg,
|
||||
} from "./utils";
|
||||
|
||||
vi.mock("@calcom/lib/server/queries", () => {
|
||||
return {
|
||||
|
@ -19,7 +30,14 @@ vi.mock("@calcom/lib/server/queries/organisations", () => {
|
|||
};
|
||||
});
|
||||
|
||||
const mockedReturnSuccessCheckPerms = { accepted: true, disableImpersonation: false, id: 1, role: MembershipRole.ADMIN, userId: 1, teamId: 1 }
|
||||
const mockedReturnSuccessCheckPerms = {
|
||||
accepted: true,
|
||||
disableImpersonation: false,
|
||||
id: 1,
|
||||
role: MembershipRole.ADMIN,
|
||||
userId: 1,
|
||||
teamId: 1,
|
||||
};
|
||||
|
||||
const mockedTeam: TeamWithParent = {
|
||||
id: 1,
|
||||
|
@ -41,20 +59,20 @@ const mockedTeam: TeamWithParent = {
|
|||
metadata: null,
|
||||
parentId: null,
|
||||
parent: null,
|
||||
isPrivate:false
|
||||
isPrivate: false,
|
||||
};
|
||||
|
||||
const mockUser: User = {
|
||||
id: 4,
|
||||
username: 'pro',
|
||||
name: 'Pro Example',
|
||||
email: 'pro@example.com',
|
||||
username: "pro",
|
||||
name: "Pro Example",
|
||||
email: "pro@example.com",
|
||||
emailVerified: new Date(),
|
||||
password: '',
|
||||
password: "",
|
||||
bio: null,
|
||||
avatar: null,
|
||||
timeZone: 'Europe/London',
|
||||
weekStart: 'Sunday',
|
||||
timeZone: "Europe/London",
|
||||
weekStart: "Sunday",
|
||||
startTime: 0,
|
||||
endTime: 1440,
|
||||
bufferTime: 0,
|
||||
|
@ -64,78 +82,76 @@ const mockUser: User = {
|
|||
trialEndsAt: null,
|
||||
defaultScheduleId: null,
|
||||
completedOnboarding: true,
|
||||
locale: 'en',
|
||||
locale: "en",
|
||||
timeFormat: 12,
|
||||
twoFactorSecret: null,
|
||||
twoFactorEnabled: false,
|
||||
identityProvider: 'CAL',
|
||||
identityProvider: "CAL",
|
||||
identityProviderId: null,
|
||||
invitedTo: null,
|
||||
brandColor: '#292929',
|
||||
darkBrandColor: '#fafafa',
|
||||
brandColor: "#292929",
|
||||
darkBrandColor: "#fafafa",
|
||||
away: false,
|
||||
allowDynamicBooking: true,
|
||||
metadata: null,
|
||||
verified: false,
|
||||
role: 'USER',
|
||||
role: "USER",
|
||||
disableImpersonation: false,
|
||||
organizationId: null
|
||||
}
|
||||
organizationId: null,
|
||||
};
|
||||
|
||||
describe("Invite Member Utils", () => {
|
||||
describe("checkPermissions", () => {
|
||||
it("It should throw an error if the user is not an admin of the ORG", async () => {
|
||||
vi.mocked(isOrganisationAdmin).mockResolvedValue(false);
|
||||
await expect(checkPermissions({ userId: 1, teamId: 1, isOrg: true })).rejects.toThrow();
|
||||
})
|
||||
});
|
||||
it("It should NOT throw an error if the user is an admin of the ORG", async () => {
|
||||
vi.mocked(isOrganisationAdmin).mockResolvedValue(mockedReturnSuccessCheckPerms);
|
||||
await expect(checkPermissions({ userId: 1, teamId: 1, isOrg: true })).resolves.not.toThrow();
|
||||
})
|
||||
});
|
||||
it("It should throw an error if the user is not an admin of the team", async () => {
|
||||
vi.mocked(isTeamAdmin).mockResolvedValue(false);
|
||||
await expect(checkPermissions({ userId: 1, teamId: 1 })).rejects.toThrow();
|
||||
})
|
||||
});
|
||||
it("It should NOT throw an error if the user is an admin of a team", async () => {
|
||||
vi.mocked(isTeamAdmin).mockResolvedValue(mockedReturnSuccessCheckPerms);
|
||||
await expect(checkPermissions({ userId: 1, teamId: 1 })).resolves.not.toThrow();
|
||||
})
|
||||
})
|
||||
describe('getEmailsToInvite', () => {
|
||||
it('should throw a TRPCError with code BAD_REQUEST if no emails are provided', async () => {
|
||||
});
|
||||
});
|
||||
describe("getEmailsToInvite", () => {
|
||||
it("should throw a TRPCError with code BAD_REQUEST if no emails are provided", async () => {
|
||||
await expect(getEmailsToInvite([])).rejects.toThrow(TRPCError);
|
||||
});
|
||||
|
||||
it('should return an array with one email if a string is provided', async () => {
|
||||
const result = await getEmailsToInvite('test@example.com');
|
||||
expect(result).toEqual(['test@example.com']);
|
||||
it("should return an array with one email if a string is provided", async () => {
|
||||
const result = await getEmailsToInvite("test@example.com");
|
||||
expect(result).toEqual(["test@example.com"]);
|
||||
});
|
||||
|
||||
it('should return an array with multiple emails if an array is provided', async () => {
|
||||
const result = await getEmailsToInvite(['test1@example.com', 'test2@example.com']);
|
||||
expect(result).toEqual(['test1@example.com', 'test2@example.com']);
|
||||
it("should return an array with multiple emails if an array is provided", async () => {
|
||||
const result = await getEmailsToInvite(["test1@example.com", "test2@example.com"]);
|
||||
expect(result).toEqual(["test1@example.com", "test2@example.com"]);
|
||||
});
|
||||
});
|
||||
describe('checkInputEmailIsValid', () => {
|
||||
it('should throw a TRPCError with code BAD_REQUEST if the email is invalid', () => {
|
||||
const invalidEmail = 'invalid-email';
|
||||
describe("checkInputEmailIsValid", () => {
|
||||
it("should throw a TRPCError with code BAD_REQUEST if the email is invalid", () => {
|
||||
const invalidEmail = "invalid-email";
|
||||
expect(() => checkInputEmailIsValid(invalidEmail)).toThrow(TRPCError);
|
||||
expect(() => checkInputEmailIsValid(invalidEmail)).toThrowError(
|
||||
'Invite failed because invalid-email is not a valid email address'
|
||||
"Invite failed because invalid-email is not a valid email address"
|
||||
);
|
||||
});
|
||||
|
||||
it('should not throw an error if the email is valid', () => {
|
||||
const validEmail = 'valid-email@example.com';
|
||||
it("should not throw an error if the email is valid", () => {
|
||||
const validEmail = "valid-email@example.com";
|
||||
expect(() => checkInputEmailIsValid(validEmail)).not.toThrow();
|
||||
});
|
||||
});
|
||||
describe("getOrgConnectionInfo", () => {
|
||||
|
||||
const orgAutoAcceptDomain = "example.com";
|
||||
const usersEmail = "user@example.com";
|
||||
|
||||
|
||||
it("should return orgId and autoAccept as true if team has parent and usersEmail domain matches orgAutoAcceptDomain and orgVerified is true", () => {
|
||||
const result = getOrgConnectionInfo({
|
||||
orgAutoAcceptDomain,
|
||||
|
@ -264,7 +280,6 @@ describe("Invite Member Utils", () => {
|
|||
};
|
||||
const isOrg = false;
|
||||
|
||||
|
||||
it("should not throw when inviting an existing user to the same organization", () => {
|
||||
const inviteeWithOrg: User = {
|
||||
...invitee,
|
||||
|
@ -273,10 +288,8 @@ describe("Invite Member Utils", () => {
|
|||
const teamWithOrg = {
|
||||
...mockedTeam,
|
||||
parentId: 2,
|
||||
}
|
||||
expect(() =>
|
||||
throwIfInviteIsToOrgAndUserExists(inviteeWithOrg, teamWithOrg, isOrg)
|
||||
).not.toThrow();
|
||||
};
|
||||
expect(() => throwIfInviteIsToOrgAndUserExists(inviteeWithOrg, teamWithOrg, isOrg)).not.toThrow();
|
||||
});
|
||||
it("should throw a TRPCError with code FORBIDDEN if the invitee is already a member of another organization", () => {
|
||||
const inviteeWithOrg: User = {
|
||||
|
@ -286,23 +299,17 @@ describe("Invite Member Utils", () => {
|
|||
const teamWithOrg = {
|
||||
...mockedTeam,
|
||||
parentId: 3,
|
||||
}
|
||||
expect(() =>
|
||||
throwIfInviteIsToOrgAndUserExists(inviteeWithOrg, teamWithOrg, isOrg)
|
||||
).toThrow(TRPCError);
|
||||
};
|
||||
expect(() => throwIfInviteIsToOrgAndUserExists(inviteeWithOrg, teamWithOrg, isOrg)).toThrow(TRPCError);
|
||||
});
|
||||
|
||||
it("should throw a TRPCError with code FORBIDDEN if the invitee already exists in Cal.com and is being invited to an organization", () => {
|
||||
const isOrg = true;
|
||||
expect(() =>
|
||||
throwIfInviteIsToOrgAndUserExists(invitee, mockedTeam, isOrg)
|
||||
).toThrow(TRPCError);
|
||||
expect(() => throwIfInviteIsToOrgAndUserExists(invitee, mockedTeam, isOrg)).toThrow(TRPCError);
|
||||
});
|
||||
|
||||
it("should not throw an error if the invitee does not already belong to another organization and is not being invited to an organization", () => {
|
||||
expect(() =>
|
||||
throwIfInviteIsToOrgAndUserExists(invitee, mockedTeam, isOrg)
|
||||
).not.toThrow();
|
||||
expect(() => throwIfInviteIsToOrgAndUserExists(invitee, mockedTeam, isOrg)).not.toThrow();
|
||||
});
|
||||
});
|
||||
describe("createAndAutoJoinIfInOrg", () => {
|
||||
|
@ -314,7 +321,7 @@ describe("Invite Member Utils", () => {
|
|||
});
|
||||
expect(result).toEqual({ autoJoined: false });
|
||||
});
|
||||
|
||||
|
||||
it("should return autoJoined: false if the team does not have a parent organization", async () => {
|
||||
const result = await createAndAutoJoinIfInOrg({
|
||||
team: { ...mockedTeam, parentId: null },
|
||||
|
@ -325,4 +332,4 @@ describe("Invite Member Utils", () => {
|
|||
});
|
||||
// TODO: Add test for when the user is already a member of the organization - need to mock prisma response value
|
||||
});
|
||||
})
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue