diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..66895f52 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +@acm-uiuc:registry=https://registry.npmjs.org diff --git a/package.json b/package.json index 6caff875..7ab57674 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,9 @@ "test:e2e": "playwright test", "test:e2e-ui": "playwright test --ui" }, - "dependencies": {}, + "dependencies": { + "@acm-uiuc/js-shared": "^2.1.0" + }, "devDependencies": { "@eslint/compat": "^1.2.8", "@eslint/eslintrc": "^3.3.1", @@ -87,4 +89,4 @@ "resolutions": { "pdfjs-dist": "^4.8.69" } -} +} \ No newline at end of file diff --git a/src/api/routes/events.ts b/src/api/routes/events.ts index 2480fce9..04af6508 100644 --- a/src/api/routes/events.ts +++ b/src/api/routes/events.ts @@ -2,7 +2,7 @@ import "zod-openapi/extend"; import { FastifyPluginAsync, FastifyRequest } from "fastify"; import { AppRoles } from "../../common/roles.js"; import { z } from "zod"; -import { OrganizationList } from "../../common/orgs.js"; +import { CoreOrganizationList } from "@acm-uiuc/js-shared"; import { DeleteItemCommand, GetItemCommand, @@ -111,7 +111,7 @@ const baseSchema = z.object({ description: "Google Maps link for easy navigation to the event location.", example: "https://maps.app.goo.gl/dwbBBBkfjkgj8gvA8", }), - host: z.enum(OrganizationList as [string, ...string[]]), + host: z.enum(CoreOrganizationList as [string, ...string[]]), featured: z.boolean().default(false).openapi({ description: "Whether or not the event should be shown on the ACM @ UIUC website home page (and added to Discord, as available).", @@ -165,7 +165,7 @@ const eventsPlugin: FastifyPluginAsyncZodOpenApi = async ( "If true, only get events which are marked as featured.", }), host: z - .enum(OrganizationList as [string, ...string[]]) + .enum(CoreOrganizationList as [string, ...string[]]) .optional() .openapi({ description: "Retrieve events only for a specific host.", diff --git a/src/api/routes/ics.ts b/src/api/routes/ics.ts index bdc422dd..3f7b36d8 100644 --- a/src/api/routes/ics.ts +++ b/src/api/routes/ics.ts @@ -14,7 +14,7 @@ import ical, { } from "ical-generator"; import moment from "moment"; import { getVtimezoneComponent } from "@touch4it/ical-timezones"; -import { OrganizationList } from "../../common/orgs.js"; +import { CoreOrganizationList } from "@acm-uiuc/js-shared"; import { CLIENT_HTTP_CACHE_POLICY, EventRepeatOptions } from "./events.js"; import rateLimiter from "api/plugins/rateLimiter.js"; import { getCacheCounter } from "api/functions/cache.js"; @@ -43,7 +43,7 @@ function generateHostName(host: string) { const icalPlugin: FastifyPluginAsync = async (fastify, _options) => { fastify.register(rateLimiter, { - limit: OrganizationList.length, + limit: CoreOrganizationList.length, duration: 30, rateLimitIdentifier: "ical", }); @@ -53,7 +53,7 @@ const icalPlugin: FastifyPluginAsync = async (fastify, _options) => { schema: withTags(["iCalendar Integration"], { params: z.object({ host: z - .optional(z.enum(OrganizationList as [string, ...string[]])) + .optional(z.enum(CoreOrganizationList as [string, ...string[]])) .openapi({ description: "Host to get calendar for." }), }), summary: @@ -87,7 +87,7 @@ const icalPlugin: FastifyPluginAsync = async (fastify, _options) => { reply.header("etag", etag); } if (host) { - if (!OrganizationList.includes(host)) { + if (!CoreOrganizationList.includes(host)) { throw new ValidationError({ message: `Invalid host parameter "${host}" in path.`, }); diff --git a/src/api/routes/organizations.ts b/src/api/routes/organizations.ts index 4d86191a..830d7de1 100644 --- a/src/api/routes/organizations.ts +++ b/src/api/routes/organizations.ts @@ -1,5 +1,5 @@ import { FastifyPluginAsync } from "fastify"; -import { OrganizationList } from "../../common/orgs.js"; +import { AllOrganizationList } from "@acm-uiuc/js-shared"; import fastifyCaching from "@fastify/caching"; import rateLimiter from "api/plugins/rateLimiter.js"; import { withTags } from "api/components/index.js"; @@ -23,7 +23,7 @@ const organizationsPlugin: FastifyPluginAsync = async (fastify, _options) => { }), }, async (_request, reply) => { - reply.send(OrganizationList); + reply.send(AllOrganizationList); }, ); }; diff --git a/src/common/orgs.ts b/src/common/orgs.ts deleted file mode 100644 index 61d570d2..00000000 --- a/src/common/orgs.ts +++ /dev/null @@ -1,30 +0,0 @@ -export const SIGList = [ - "SIGPwny", - "SIGCHI", - "GameBuilders", - "SIGAIDA", - "SIGGRAPH", - "ICPC", - "SIGMobile", - "SIGMusic", - "GLUG", - "SIGNLL", - "SIGma", - "SIGQuantum", - "SIGecom", - "SIGPLAN", - "SIGPolicy", - "SIGARCH", - "SIGRobotics", - "SIGtricity", -] as [string, ...string[]]; - -export const CommitteeList = [ - "Infrastructure Committee", - "Social Committee", - "Mentorship Committee", - "Academic Committee", - "Corporate Committee", - "Marketing Committee", -] as [string, ...string[]]; -export const OrganizationList = ["ACM", ...SIGList, ...CommitteeList] as [string, ...string[]]; diff --git a/src/common/policies/events.ts b/src/common/policies/events.ts index e31ca752..d670d3e2 100644 --- a/src/common/policies/events.ts +++ b/src/common/policies/events.ts @@ -1,11 +1,11 @@ -import { z } from "zod"; +import { string, z } from "zod"; import { createPolicy } from "./evaluator.js"; -import { OrganizationList } from "../orgs.js"; +import { CoreOrganizationList } from "@acm-uiuc/js-shared"; import { FastifyRequest } from "fastify"; export const hostRestrictionPolicy = createPolicy( "EventsHostRestrictionPolicy", - z.object({ host: z.array(z.enum(OrganizationList)) }), + z.object({ host: z.array(z.enum(CoreOrganizationList)) }), (request: FastifyRequest & { username?: string }, params) => { if (request.method === "GET") { return { diff --git a/src/common/types/roomRequest.ts b/src/common/types/roomRequest.ts index ef2d64a7..c536571f 100644 --- a/src/common/types/roomRequest.ts +++ b/src/common/types/roomRequest.ts @@ -1,5 +1,5 @@ import { z } from "zod"; -import { OrganizationList } from "../orgs.js"; +import { AllOrganizationList } from "@acm-uiuc/js-shared"; export const eventThemeOptions = [ "Arts & Music", @@ -146,7 +146,7 @@ export const roomRequestPostResponse = z.object({ }); export const roomRequestBaseSchema = z.object({ - host: z.enum(OrganizationList), + host: z.enum(AllOrganizationList), title: z.string().min(2, "Title must have at least 2 characters"), semester: z .string() diff --git a/src/ui/pages/events/ManageEvent.page.tsx b/src/ui/pages/events/ManageEvent.page.tsx index 0780855d..c44f25b2 100644 --- a/src/ui/pages/events/ManageEvent.page.tsx +++ b/src/ui/pages/events/ManageEvent.page.tsx @@ -20,7 +20,7 @@ import { useNavigate, useParams } from "react-router-dom"; import { z } from "zod"; import { AuthGuard } from "@ui/components/AuthGuard"; import { useApi } from "@ui/util/api"; -import { OrganizationList as orgList } from "@common/orgs"; +import { AllOrganizationList as orgList } from "@acm-uiuc/js-shared"; import { AppRoles } from "@common/roles"; import { EVENT_CACHED_DURATION } from "@common/config"; import { IconPlus, IconTrash } from "@tabler/icons-react"; diff --git a/src/ui/pages/roomRequest/NewRoomRequest.tsx b/src/ui/pages/roomRequest/NewRoomRequest.tsx index b64867b5..bfe293df 100644 --- a/src/ui/pages/roomRequest/NewRoomRequest.tsx +++ b/src/ui/pages/roomRequest/NewRoomRequest.tsx @@ -18,7 +18,7 @@ import { } from "@mantine/core"; import { useForm, zodResolver } from "@mantine/form"; import { DateInput, DateTimePicker } from "@mantine/dates"; -import { OrganizationList } from "@common/orgs"; +import { AllOrganizationList } from "@acm-uiuc/js-shared"; import { eventThemeOptions, spaceTypeOptions, @@ -401,7 +401,10 @@ const NewRoomRequest: React.FC = ({ placeholder="Select host organization" withAsterisk searchable - data={OrganizationList.map((org) => ({ value: org, label: org }))} + data={AllOrganizationList.map((org) => ({ + value: org, + label: org, + }))} {...form.getInputProps("host")} /> new Promise((resolve) => setTimeout(resolve, ms)); const fetchWithRateLimit = async (url: string) => { const response = await fetch(url); - expect(response.status).toBe(200); // Check rate limit headers const remaining = parseInt( @@ -27,19 +25,29 @@ const fetchWithRateLimit = async (url: string) => { return response; }; -test("Get calendars with rate limit handling", { timeout: 45000 }, async () => { - for (const org of OrganizationList) { - const response = await fetchWithRateLimit( - `${baseEndpoint}/api/v1/ical/${org}`, - ); - expect(response.status).toBe(200); - expect(response.headers.get("Content-Disposition")).toEqual( - 'attachment; filename="calendar.ics"', - ); - const calendar = ical.sync.parseICS(await response.text()); - expect(calendar["vcalendar"]["type"]).toEqual("VCALENDAR"); - } -}); +describe( + "Get calendars per organization with rate limit handling", + { timeout: 450000 }, + async () => { + for (const org of CoreOrganizationList) { + test(`Get ${org} calendar`, async () => { + await delay(Math.random() * 200); + const response = await fetchWithRateLimit( + `${baseEndpoint}/api/v1/ical/${org}`, + ); + if (!response.ok) { + console.log(response); + } + expect(response.status).toBe(200); + expect(response.headers.get("Content-Disposition")).toEqual( + 'attachment; filename="calendar.ics"', + ); + const calendar = ical.sync.parseICS(await response.text()); + expect(calendar["vcalendar"]["type"]).toEqual("VCALENDAR"); + }); + } + }, +); test("Check that the ical base works", { timeout: 45000 }, async () => { const response = await fetchWithRateLimit( diff --git a/yarn.lock b/yarn.lock index 4635aff9..0d0e60c3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@acm-uiuc/js-shared@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@acm-uiuc/js-shared/-/js-shared-2.1.0.tgz#c70f6d41a18daa4c2eb5f8be5de9e7a323e8f84c" + integrity sha512-d8JX9lXaVoEnA5oVDniaoHj0JLayA78uCFAWhmpF6BxxDpQPm7sLd6aeUfhdfrkvkWFq4AYBMTiSwYXKQsVbuA== + "@adobe/css-tools@^4.4.0": version "4.4.1" resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.1.tgz#2447a230bfe072c1659e6815129c03cf170710e3"