diff --git a/apps/api/package.json b/apps/api/package.json index e79062b..a45a6a1 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -31,6 +31,7 @@ "@date-fns/tz": "^1.2.0", "@date-fns/utc": "^2.1.0", "@envelop/generic-auth": "^7.0.0", + "@envelop/rate-limiter": "^6.2.1", "@graphql-tools/load-files": "^7.0.0", "@graphql-tools/merge": "^9.0.0", "@logtail/pino": "^0.4.15", diff --git a/apps/api/src/app/digests/services/digest.service.ts b/apps/api/src/app/digests/services/digest.service.ts index c4eb868..5d66539 100644 --- a/apps/api/src/app/digests/services/digest.service.ts +++ b/apps/api/src/app/digests/services/digest.service.ts @@ -5,9 +5,12 @@ import { FindDigestByTypeArgs, UpsertDigest, Digest, + DigestWithRelations, } from "./digest.types"; import { assign, isObject } from "radash"; import { DigestType } from "@prisma/client"; +import { sendTeamMetricsDigest } from "./digest-team-metrics.service"; +import { sendTeamWipDigest } from "./digest-team-wip.service"; export const findDigestByType = async ({ workspaceId, @@ -100,3 +103,13 @@ export const upsertDigest = async ({ return updatedDigest as unknown as Digest; }; + +export const sendDigest = async (digest: DigestWithRelations) => { + if (digest.type === DigestType.TEAM_METRICS) { + await sendTeamMetricsDigest(digest); + } + + if (digest.type === DigestType.TEAM_WIP) { + await sendTeamWipDigest(digest); + } +}; diff --git a/apps/api/src/app/digests/workers/digest-send.worker.ts b/apps/api/src/app/digests/workers/digest-send.worker.ts index c2c9ada..c0c8c81 100644 --- a/apps/api/src/app/digests/workers/digest-send.worker.ts +++ b/apps/api/src/app/digests/workers/digest-send.worker.ts @@ -3,10 +3,8 @@ import { SweetQueue } from "../../../bull-mq/queues"; import { createWorker } from "../../../bull-mq/workers"; import { logger } from "../../../lib/logger"; import { DigestWithRelations } from "../services/digest.types"; -import { DigestType } from "@prisma/client"; -import { sendTeamWipDigest } from "../services/digest-team-wip.service"; -import { sendTeamMetricsDigest } from "../services/digest-team-metrics.service"; import { InputValidationException } from "../../errors/exceptions/input-validation.exception"; +import { sendDigest } from "../services/digest.service"; export const digestSendWorker = createWorker( SweetQueue.DIGEST_SEND, @@ -21,12 +19,6 @@ export const digestSendWorker = createWorker( }); } - if (digest.type === DigestType.TEAM_METRICS) { - await sendTeamMetricsDigest(digest); - } - - if (digest.type === DigestType.TEAM_WIP) { - await sendTeamWipDigest(digest); - } + await sendDigest(digest); } ); diff --git a/apps/api/src/app/directives.schema.ts b/apps/api/src/app/directives.schema.ts new file mode 100644 index 0000000..71fc834 --- /dev/null +++ b/apps/api/src/app/directives.schema.ts @@ -0,0 +1,9 @@ +export default /* GraphQL */ ` + directive @rateLimit( + max: Int + window: String + message: String + identityArgs: [String] + arrayLengthField: String + ) on FIELD_DEFINITION +`; diff --git a/apps/api/src/app/errors/exceptions/rate-limit.exception.ts b/apps/api/src/app/errors/exceptions/rate-limit.exception.ts new file mode 100644 index 0000000..d19da09 --- /dev/null +++ b/apps/api/src/app/errors/exceptions/rate-limit.exception.ts @@ -0,0 +1,21 @@ +import { + BaseException, + BaseExceptionOptions, + ErrorCode, +} from "./base.exception"; + +export class RateLimitException extends BaseException { + name = "RateLimitException"; + + constructor( + message = "Rate limit exceeded", + options?: Partial + ) { + super(message, { + code: ErrorCode.RATE_LIMIT_EXCEEDED, + userFacingMessage: "You have exceeded the rate limit.", + severity: "log", + ...options, + }); + } +} diff --git a/apps/api/src/app/integrations/resolvers/integrations.schema.ts b/apps/api/src/app/integrations/resolvers/integrations.schema.ts index 1c24d70..fe6c521 100644 --- a/apps/api/src/app/integrations/resolvers/integrations.schema.ts +++ b/apps/api/src/app/integrations/resolvers/integrations.schema.ts @@ -23,6 +23,12 @@ export default /* GraphQL */ ` app: IntegrationApp! } + input SendTestMessageInput { + workspaceId: SweetID! + app: IntegrationApp! + channel: String! + } + extend type Workspace { integrations: [Integration!]! } @@ -30,5 +36,11 @@ export default /* GraphQL */ ` type Mutation { installIntegration(input: InstallIntegrationInput!): Void removeIntegration(input: RemoveIntegrationInput!): Void + sendTestMessage(input: SendTestMessageInput!): Void + @rateLimit( + window: "60s" + max: 5 + message: "You got rate limited. You can send 5 test messages per minute." + ) } `; diff --git a/apps/api/src/app/integrations/resolvers/mutations/send-test-message.mutation.ts b/apps/api/src/app/integrations/resolvers/mutations/send-test-message.mutation.ts new file mode 100644 index 0000000..6df4b3e --- /dev/null +++ b/apps/api/src/app/integrations/resolvers/mutations/send-test-message.mutation.ts @@ -0,0 +1,34 @@ +import { z } from "zod"; +import { createMutationResolver } from "../../../../lib/graphql"; +import { logger } from "../../../../lib/logger"; +import { validateInputOrThrow } from "../../../../lib/validate-input"; +import { protectWithPaywall } from "../../../billing/services/billing.service"; +import { authorizeWorkspaceOrThrow } from "../../../workspace-authorization.service"; +import { sendTestMessage } from "../../slack/services/slack-integration.service"; +import { IntegrationApp } from "@sweetr/graphql-types/api"; + +export const sendTestMessageMutation = createMutationResolver({ + sendTestMessage: async (_, { input }, context) => { + logger.info("mutation.sendTestMessage", { input }); + + validateInputOrThrow( + z.object({ + workspaceId: z.number(), + channel: z.string().max(80), + app: z.nativeEnum(IntegrationApp), + }), + input + ); + + await authorizeWorkspaceOrThrow({ + workspaceId: input.workspaceId, + gitProfileId: context.currentToken.gitProfileId, + }); + + await protectWithPaywall(input.workspaceId); + + await sendTestMessage(input.workspaceId, input.channel); + + return null; + }, +}); diff --git a/apps/api/src/app/integrations/slack/services/slack-client.service.ts b/apps/api/src/app/integrations/slack/services/slack-client.service.ts index 62c10b3..14c6369 100644 --- a/apps/api/src/app/integrations/slack/services/slack-client.service.ts +++ b/apps/api/src/app/integrations/slack/services/slack-client.service.ts @@ -70,7 +70,9 @@ export const joinSlackChannel = async ( const channel = await findSlackChannel(slackClient, channelName); if (!channel?.id) { - throw new ResourceNotFoundException("Slack channel not found"); + throw new ResourceNotFoundException("Slack channel not found", { + userFacingMessage: "Could not find Slack channel.", + }); } if (!channel.is_member) { diff --git a/apps/api/src/app/integrations/slack/services/slack-integration.service.ts b/apps/api/src/app/integrations/slack/services/slack-integration.service.ts index e21c9c6..8205669 100644 --- a/apps/api/src/app/integrations/slack/services/slack-integration.service.ts +++ b/apps/api/src/app/integrations/slack/services/slack-integration.service.ts @@ -11,8 +11,11 @@ import { authorizeSlackWorkspace, getSlackClient, getWorkspaceSlackClient, + joinSlackChannel, + sendSlackMessage, uninstallSlackWorkspace, } from "./slack-client.service"; +import { ResourceNotFoundException } from "../../../errors/exceptions/resource-not-found.exception"; export const installIntegration = async (workspaceId: number, code: string) => { let response: OauthV2AccessResponse; @@ -122,3 +125,20 @@ export const getInstallUrl = (): string => { return url.toString(); }; + +export const sendTestMessage = async (workspaceId: number, channel: string) => { + const { slackClient } = await getWorkspaceSlackClient(workspaceId); + + const slackChannel = await joinSlackChannel(slackClient, channel); + + if (!slackChannel?.id) { + throw new ResourceNotFoundException("Slack channel not found"); + } + + await sendSlackMessage(slackClient, { + channel: slackChannel.id, + text: "Sweet, it works! 🍧", + unfurl_links: false, + unfurl_media: false, + }); +}; diff --git a/apps/api/src/lib/rate-limiter.plugin.ts b/apps/api/src/lib/rate-limiter.plugin.ts new file mode 100644 index 0000000..88a3f53 --- /dev/null +++ b/apps/api/src/lib/rate-limiter.plugin.ts @@ -0,0 +1,12 @@ +import { useRateLimiter } from "@envelop/rate-limiter"; +import { GraphQLContext } from "@sweetr/graphql-types/shared"; +import { RateLimitException } from "../app/errors/exceptions/rate-limit.exception"; + +export const rateLimiterPlugin = useRateLimiter({ + identifyFn: (context: GraphQLContext) => + context.workspaceId?.toString() ?? context.currentToken?.userId.toString(), + transformError: (message: string) => + new RateLimitException("Rate limit exceeded", { + userFacingMessage: message, + }), +}); diff --git a/apps/api/src/yoga.ts b/apps/api/src/yoga.ts index 039c85c..9d698fa 100644 --- a/apps/api/src/yoga.ts +++ b/apps/api/src/yoga.ts @@ -6,6 +6,7 @@ import { join } from "path"; import { authPlugin } from "./app/auth/resolvers/plugins/auth.plugin"; import { env } from "./env"; import { useSentry } from "./lib/use-sentry.plugin"; +import { rateLimiterPlugin } from "./lib/rate-limiter.plugin"; const resolvers = loadFilesSync( join(__dirname, "./**/*.(query|mutation|resolver).(js|ts)") @@ -21,7 +22,7 @@ const schema = createSchema({ export const yoga = createYoga({ graphqlEndpoint: "/", schema, - plugins: [authPlugin, useSentry()], + plugins: [authPlugin, useSentry(), rateLimiterPlugin], graphiql: env.NODE_ENV === "development", landingPage: false, logging: false, diff --git a/apps/web/src/api/integrations.api.ts b/apps/web/src/api/integrations.api.ts index e0db6a6..bdd84fd 100644 --- a/apps/web/src/api/integrations.api.ts +++ b/apps/web/src/api/integrations.api.ts @@ -10,7 +10,9 @@ import { InstallIntegrationMutation, MutationInstallIntegrationArgs, MutationRemoveIntegrationArgs, + MutationSendTestMessageArgs, RemoveIntegrationMutation, + SendTestMessageMutation, WorkspaceIntegrationsQuery, WorkspaceIntegrationsQueryVariables, } from "@sweetr/graphql-types/frontend/graphql"; @@ -93,3 +95,24 @@ export const useRemoveIntegrationMutation = ( }, ...options, }); + +export const useSendTestMessageMutation = ( + options?: UseMutationOptions< + SendTestMessageMutation, + unknown, + MutationSendTestMessageArgs, + unknown + >, +) => + useMutation({ + mutationFn: (args) => + graphQLClient.request( + graphql(/* GraphQL */ ` + mutation SendTestMessage($input: SendTestMessageInput!) { + sendTestMessage(input: $input) + } + `), + args, + ), + ...options, + }); diff --git a/apps/web/src/app/teams/[id]/digests/settings/components/digest-base-fields/digest-base-fields.tsx b/apps/web/src/app/teams/[id]/digests/settings/components/digest-base-fields/digest-base-fields.tsx index 7358691..ecf64a9 100644 --- a/apps/web/src/app/teams/[id]/digests/settings/components/digest-base-fields/digest-base-fields.tsx +++ b/apps/web/src/app/teams/[id]/digests/settings/components/digest-base-fields/digest-base-fields.tsx @@ -12,24 +12,32 @@ import { Group, Input, Text, + Button, + Tooltip, } from "@mantine/core"; import { DigestFrequency } from "@sweetr/graphql-types/api"; -import { IconClock, IconInfoCircle, IconWorld } from "@tabler/icons-react"; +import { + IconBrandSlack, + IconClock, + IconInfoCircle, + IconWorld, +} from "@tabler/icons-react"; import { BoxSetting } from "../../../../../../../components/box-setting"; import { SelectHour } from "../../../../../../../components/select-hour"; import { SelectTimezone } from "../../../../../../../components/select-timezone/select-timezone"; import { DayOfTheWeek } from "@sweetr/graphql-types/frontend/graphql"; import { useEffect, useRef } from "react"; +import { useSendTestMessage } from "../../../../use-send-test-message"; interface DigestBaseFieldsProps { form: UseFormReturnType; } export const DigestBaseFields = ({ form }: DigestBaseFieldsProps) => { + const { sendTestMessage, isSendingTestMessage } = useSendTestMessage(); const isEnabled = form.values.enabled; const channelRef = useRef(null); - useEffect(() => { if (isEnabled && !form.values.channel) { channelRef.current?.focus(); @@ -69,6 +77,18 @@ export const DigestBaseFields = ({ form }: DigestBaseFieldsProps) => { )} + + + diff --git a/apps/web/src/app/teams/[id]/use-send-test-message.ts b/apps/web/src/app/teams/[id]/use-send-test-message.ts new file mode 100644 index 0000000..6f28c34 --- /dev/null +++ b/apps/web/src/app/teams/[id]/use-send-test-message.ts @@ -0,0 +1,39 @@ +import { IntegrationApp } from "@sweetr/graphql-types/api"; +import { showSuccessNotification } from "../../../providers/notification.provider"; +import { getErrorMessage } from "../../../providers/error-message.provider"; +import { useSendTestMessageMutation } from "../../../api/integrations.api"; +import { useWorkspace } from "../../../providers/workspace.provider"; +import { showErrorNotification } from "../../../providers/notification.provider"; + +export const useSendTestMessage = () => { + const { workspace } = useWorkspace(); + const { mutate, isPending: isSendingTestMessage } = + useSendTestMessageMutation(); + + const sendTestMessage = (channel: string) => { + mutate( + { + input: { + workspaceId: workspace.id, + app: IntegrationApp.SLACK, + channel: channel, + }, + }, + { + onError: (error) => { + showErrorNotification({ + message: getErrorMessage(error), + }); + }, + onSuccess: () => { + showSuccessNotification({ message: "Test message sent" }); + }, + }, + ); + }; + + return { + sendTestMessage, + isSendingTestMessage, + }; +}; diff --git a/apps/web/src/providers/error-message.provider.ts b/apps/web/src/providers/error-message.provider.ts index 36b18e3..3d4b299 100644 --- a/apps/web/src/providers/error-message.provider.ts +++ b/apps/web/src/providers/error-message.provider.ts @@ -15,7 +15,14 @@ export const getErrorMessage = (error: unknown) => { } | undefined; - if (extensions?.code === "INPUT_VALIDATION_FAILED") { + if ( + extensions && + [ + "INPUT_VALIDATION_FAILED", + "RESOURCE_NOT_FOUND", + "RATE_LIMIT_EXCEEDED", + ].includes(extensions.code) + ) { return extensions.userFacingMessage; } } diff --git a/package-lock.json b/package-lock.json index ced847c..9d3e02e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "@date-fns/tz": "^1.2.0", "@date-fns/utc": "^2.1.0", "@envelop/generic-auth": "^7.0.0", + "@envelop/rate-limiter": "^6.2.1", "@graphql-tools/load-files": "^7.0.0", "@graphql-tools/merge": "^9.0.0", "@logtail/pino": "^0.4.15", @@ -81,18 +82,6 @@ "typescript": "^5.2.2" } }, - "apps/api/node_modules/@envelop/core": { - "version": "5.0.0", - "license": "MIT", - "peer": true, - "dependencies": { - "@envelop/types": "5.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, "apps/api/node_modules/@envelop/extended-validation": { "version": "4.0.0", "license": "MIT", @@ -124,17 +113,6 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" } }, - "apps/api/node_modules/@envelop/types": { - "version": "5.0.0", - "license": "MIT", - "peer": true, - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, "apps/api/node_modules/markdown-table": { "version": "2.0.0", "license": "MIT", @@ -622,6 +600,22 @@ "node": ">=6.0.0" } }, + "node_modules/@ardatan/aggregate-error": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@ardatan/aggregate-error/-/aggregate-error-0.0.6.tgz", + "integrity": "sha512-vyrkEHG1jrukmzTPtyWB4NLPauUw5bQeg4uhn8f+1SSynmrOcyvlb1GKQjjgoBzElLdfXCRYX8UnBlhklOHYRQ==", + "dependencies": { + "tslib": "~2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@ardatan/aggregate-error/node_modules/tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + }, "node_modules/@ardatan/relay-compiler": { "version": "12.0.0", "dev": true, @@ -1936,6 +1930,83 @@ "version": "0.3.1", "license": "MIT" }, + "node_modules/@envelop/core": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@envelop/core/-/core-5.0.3.tgz", + "integrity": "sha512-SE3JxL7odst8igN6x77QWyPpXKXz/Hs5o5Y27r+9Br6WHIhkW90lYYVITWIJQ/qYgn5PkpbaVgeFY9rgqQaZ/A==", + "dependencies": { + "@envelop/types": "5.0.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@envelop/on-resolve": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@envelop/on-resolve/-/on-resolve-4.1.1.tgz", + "integrity": "sha512-Zkc+OJMpmxStcx7DlCf/IqnKhCag8LJCfE0rzjnECdSQMBfdTY/9V5CtwdCM8kOAZAC0B4o9tVMwc/I8PfthqQ==", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@envelop/core": "^5.0.2", + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, + "node_modules/@envelop/rate-limiter": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@envelop/rate-limiter/-/rate-limiter-6.2.1.tgz", + "integrity": "sha512-SWTTKy/PtSUthQXcch18mF137R+oDo+ppYnBR7ufbRQYYbvCcb3Jlu8XkWnsIV2OIc1gUO2yHiCkNa0i9oeUJw==", + "dependencies": { + "@envelop/on-resolve": "^4.1.0", + "@graphql-tools/utils": "^10.5.4", + "graphql-middleware": "^6.1.35", + "graphql-rate-limit": "^3.3.0", + "minimatch": "^10.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@envelop/core": "^5.0.2", + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, + "node_modules/@envelop/rate-limiter/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@envelop/rate-limiter/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@envelop/types": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@envelop/types/-/types-5.0.0.tgz", + "integrity": "sha512-IPjmgSc4KpQRlO4qbEDnBEixvtb06WDmjKfi/7fkZaryh5HuOmTtixe1EupQI5XfXO8joc3d27uUZ0QdC++euA==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -3359,13 +3430,13 @@ "license": "MIT" }, "node_modules/@graphql-tools/utils": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.2.2.tgz", - "integrity": "sha512-ueoplzHIgFfxhFrF4Mf/niU/tYHuO6Uekm2nCYU72qpI+7Hn9dA2/o5XOBvFXDk27Lp5VSvQY5WfmRbqwVxaYQ==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.7.2.tgz", + "integrity": "sha512-Wn85S+hfkzfVFpXVrQ0hjnePa3p28aB6IdAGCiD1SqBCSMDRzL+OFEtyAyb30nV9Mqflqs9lCqjqlR2puG857Q==", "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", - "cross-inspect": "1.0.0", - "dset": "^3.1.2", + "cross-inspect": "1.0.1", + "dset": "^3.1.4", "tslib": "^2.4.0" }, "engines": { @@ -7935,6 +8006,11 @@ "@types/node": "*" } }, + "node_modules/@types/yup": { + "version": "0.29.13", + "resolved": "https://registry.npmjs.org/@types/yup/-/yup-0.29.13.tgz", + "integrity": "sha512-qRyuv+P/1t1JK1rA+elmK1MmCL1BapEzKKfbEhDBV/LMMse4lmhZ/XbgETI39JveDJRpLjmToOI6uFtMW/WR2g==" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.12.0", "dev": true, @@ -9328,7 +9404,6 @@ }, "node_modules/camel-case": { "version": "4.1.2", - "dev": true, "license": "MIT", "dependencies": { "pascal-case": "^3.1.2", @@ -9834,8 +9909,9 @@ } }, "node_modules/cross-inspect": { - "version": "1.0.0", - "license": "MIT", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz", + "integrity": "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==", "dependencies": { "tslib": "^2.4.0" }, @@ -10462,8 +10538,9 @@ } }, "node_modules/dset": { - "version": "3.1.3", - "license": "MIT", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", "engines": { "node": ">=4" } @@ -12199,6 +12276,144 @@ "node": ">=10" } }, + "node_modules/graphql-middleware": { + "version": "6.1.35", + "resolved": "https://registry.npmjs.org/graphql-middleware/-/graphql-middleware-6.1.35.tgz", + "integrity": "sha512-azawK7ApUYtcuPGRGBR9vDZu795pRuaFhO5fgomdJppdfKRt7jwncuh0b7+D3i574/4B+16CNWgVpnGVlg3ZCg==", + "dependencies": { + "@graphql-tools/delegate": "^8.8.1", + "@graphql-tools/schema": "^8.5.1" + }, + "peerDependencies": { + "graphql": "^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, + "node_modules/graphql-middleware/node_modules/@graphql-tools/batch-execute": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-8.5.1.tgz", + "integrity": "sha512-hRVDduX0UDEneVyEWtc2nu5H2PxpfSfM/riUlgZvo/a/nG475uyehxR5cFGvTEPEQUKY3vGIlqvtRigzqTfCew==", + "dependencies": { + "@graphql-tools/utils": "8.9.0", + "dataloader": "2.1.0", + "tslib": "^2.4.0", + "value-or-promise": "1.0.11" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/graphql-middleware/node_modules/@graphql-tools/delegate": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-8.8.1.tgz", + "integrity": "sha512-NDcg3GEQmdEHlnF7QS8b4lM1PSF+DKeFcIlLEfZFBvVq84791UtJcDj8734sIHLukmyuAxXMfA1qLd2l4lZqzA==", + "dependencies": { + "@graphql-tools/batch-execute": "8.5.1", + "@graphql-tools/schema": "8.5.1", + "@graphql-tools/utils": "8.9.0", + "dataloader": "2.1.0", + "tslib": "~2.4.0", + "value-or-promise": "1.0.11" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/graphql-middleware/node_modules/@graphql-tools/merge": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.1.tgz", + "integrity": "sha512-BMm99mqdNZbEYeTPK3it9r9S6rsZsQKtlqJsSBknAclXq2pGEfOxjcIZi+kBSkHZKPKCRrYDd5vY0+rUmIHVLg==", + "dependencies": { + "@graphql-tools/utils": "8.9.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/graphql-middleware/node_modules/@graphql-tools/schema": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-8.5.1.tgz", + "integrity": "sha512-0Esilsh0P/qYcB5DKQpiKeQs/jevzIadNTaT0jeWklPMwNbT7yMX4EqZany7mbeRRlSRwMzNzL5olyFdffHBZg==", + "dependencies": { + "@graphql-tools/merge": "8.3.1", + "@graphql-tools/utils": "8.9.0", + "tslib": "^2.4.0", + "value-or-promise": "1.0.11" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/graphql-middleware/node_modules/@graphql-tools/utils": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.9.0.tgz", + "integrity": "sha512-pjJIWH0XOVnYGXCqej8g/u/tsfV4LvLlj0eATKQu5zwnxd/TiTHq7Cg313qUPTFFHZ3PP5wJ15chYVtLDwaymg==", + "dependencies": { + "tslib": "^2.4.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/graphql-middleware/node_modules/dataloader": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.1.0.tgz", + "integrity": "sha512-qTcEYLen3r7ojZNgVUaRggOI+KM7jrKxXeSHhogh/TWxYMeONEMqY+hmkobiYQozsGIyg9OYVzO4ZIfoB4I0pQ==" + }, + "node_modules/graphql-middleware/node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" + }, + "node_modules/graphql-middleware/node_modules/value-or-promise": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.11.tgz", + "integrity": "sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/graphql-rate-limit": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/graphql-rate-limit/-/graphql-rate-limit-3.3.0.tgz", + "integrity": "sha512-mbbEv5z3SjkDLvVVdHi0XrVLavw2Mwo93GIqgQB/fx8dhcNSEv3eYI1OGdp8mhsm/MsZm7hjrRlwQMVRKBVxhA==", + "dependencies": { + "@graphql-tools/utils": "^7.6.0", + "graphql-shield": "^7.5.0", + "lodash.get": "^4.4.2", + "ms": "^2.1.3" + }, + "engines": { + "node": ">=12.0", + "pnpm": ">=6" + }, + "peerDependencies": { + "graphql": "*" + } + }, + "node_modules/graphql-rate-limit/node_modules/@graphql-tools/utils": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-7.10.0.tgz", + "integrity": "sha512-d334r6bo9mxdSqZW6zWboEnnOOFRrAPVQJ7LkU8/6grglrbcu6WhwCLzHb90E94JI3TD3ricC3YGbUqIi9Xg0w==", + "dependencies": { + "@ardatan/aggregate-error": "0.0.6", + "camel-case": "4.1.2", + "tslib": "~2.2.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0" + } + }, + "node_modules/graphql-rate-limit/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/graphql-rate-limit/node_modules/tslib": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" + }, "node_modules/graphql-request": { "version": "6.1.0", "license": "MIT", @@ -12223,6 +12438,21 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, + "node_modules/graphql-shield": { + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/graphql-shield/-/graphql-shield-7.6.5.tgz", + "integrity": "sha512-etbzf7UIhQW6vadn/UR+ds0LJOceO8ITDXwbUkQMlP2KqPgSKTZRE2zci+AUfqP+cpV9zDQdbTJfPfW5OCEamg==", + "dependencies": { + "@types/yup": "0.29.13", + "object-hash": "^3.0.0", + "tslib": "^2.4.0", + "yup": "^0.32.0" + }, + "peerDependencies": { + "graphql": "^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", + "graphql-middleware": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^6.0.0" + } + }, "node_modules/graphql-tag": { "version": "2.12.6", "dev": true, @@ -12274,27 +12504,6 @@ "graphql": "^15.2.0 || ^16.0.0" } }, - "node_modules/graphql-yoga/node_modules/@envelop/core": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "@envelop/types": "5.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/graphql-yoga/node_modules/@envelop/types": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/graphql-yoga/node_modules/@whatwg-node/events": { "version": "0.1.1", "license": "MIT", @@ -13687,10 +13896,21 @@ "version": "4.17.21", "license": "MIT" }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "node_modules/lodash.defaults": { "version": "4.2.0", "license": "MIT" }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead." + }, "node_modules/lodash.includes": { "version": "4.3.0", "license": "MIT" @@ -13802,7 +14022,6 @@ }, "node_modules/lower-case": { "version": "2.0.2", - "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.0.3" @@ -14097,6 +14316,11 @@ "thenify-all": "^1.0.0" } }, + "node_modules/nanoclone": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", + "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==" + }, "node_modules/nanoid": { "version": "3.3.7", "funding": [ @@ -14210,7 +14434,6 @@ }, "node_modules/no-case": { "version": "3.0.4", - "dev": true, "license": "MIT", "dependencies": { "lower-case": "^2.0.2", @@ -14806,7 +15029,6 @@ }, "node_modules/pascal-case": { "version": "3.1.2", - "dev": true, "license": "MIT", "dependencies": { "no-case": "^3.0.4", @@ -15511,6 +15733,11 @@ "react-is": "^16.13.1" } }, + "node_modules/property-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==" + }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -18433,6 +18660,11 @@ "node": ">=0.6" } }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" + }, "node_modules/tr46": { "version": "0.0.3", "license": "MIT" @@ -20272,6 +20504,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yup": { + "version": "0.32.11", + "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", + "integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==", + "dependencies": { + "@babel/runtime": "^7.15.4", + "@types/lodash": "^4.14.175", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "nanoclone": "^0.2.1", + "property-expr": "^2.0.4", + "toposort": "^2.0.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/zod": { "version": "3.22.4", "license": "MIT", diff --git a/packages/graphql-types/api.ts b/packages/graphql-types/api.ts index a52624b..a179f5c 100644 --- a/packages/graphql-types/api.ts +++ b/packages/graphql-types/api.ts @@ -247,6 +247,7 @@ export type Mutation = { loginToStripe?: Maybe; loginWithGithub: LoginWithGithubResponse; removeIntegration?: Maybe; + sendTestMessage?: Maybe; unarchiveTeam: Team; updateAutomation: Automation; updateDigest: Digest; @@ -280,6 +281,11 @@ export type MutationRemoveIntegrationArgs = { }; +export type MutationSendTestMessageArgs = { + input: SendTestMessageInput; +}; + + export type MutationUnarchiveTeamArgs = { input: UnarchiveTeamInput; }; @@ -515,6 +521,12 @@ export type Repository = { name: Scalars['String']['output']; }; +export type SendTestMessageInput = { + app: IntegrationApp; + channel: Scalars['String']['input']; + workspaceId: Scalars['SweetID']['input']; +}; + export type Subscription = { __typename?: 'Subscription'; isActive: Scalars['Boolean']['output']; @@ -851,6 +863,7 @@ export type ResolversTypes = { RemoveIntegrationInput: ResolverTypeWrapper>; RepositoriesQueryInput: ResolverTypeWrapper>; Repository: ResolverTypeWrapper>; + SendTestMessageInput: ResolverTypeWrapper>; String: ResolverTypeWrapper>; Subscription: ResolverTypeWrapper<{}>; SweetID: ResolverTypeWrapper>; @@ -926,6 +939,7 @@ export type ResolversParentTypes = { RemoveIntegrationInput: DeepPartial; RepositoriesQueryInput: DeepPartial; Repository: DeepPartial; + SendTestMessageInput: DeepPartial; String: DeepPartial; Subscription: {}; SweetID: DeepPartial; @@ -951,6 +965,16 @@ export type ResolversParentTypes = { WorkspaceSettingsPullRequestSizeInput: DeepPartial; }; +export type RateLimitDirectiveArgs = { + arrayLengthField?: Maybe; + identityArgs?: Maybe>>; + max?: Maybe; + message?: Maybe; + window?: Maybe; +}; + +export type RateLimitDirectiveResolver = DirectiveResolverFn; + export type SkipAuthDirectiveArgs = { }; export type SkipAuthDirectiveResolver = DirectiveResolverFn; @@ -1083,6 +1107,7 @@ export type MutationResolvers, ParentType, ContextType, RequireFields>; loginWithGithub?: Resolver>; removeIntegration?: Resolver, ParentType, ContextType, RequireFields>; + sendTestMessage?: Resolver, ParentType, ContextType, RequireFields>; unarchiveTeam?: Resolver>; updateAutomation?: Resolver>; updateDigest?: Resolver>; @@ -1320,5 +1345,6 @@ export type Resolvers = { }; export type DirectiveResolvers = { + rateLimit?: RateLimitDirectiveResolver; skipAuth?: SkipAuthDirectiveResolver; }; diff --git a/packages/graphql-types/frontend/gql.ts b/packages/graphql-types/frontend/gql.ts index 6506c9e..86c2350 100644 --- a/packages/graphql-types/frontend/gql.ts +++ b/packages/graphql-types/frontend/gql.ts @@ -33,6 +33,7 @@ const documents = { "\n query WorkspaceIntegrations($workspaceId: SweetID!) {\n workspace(workspaceId: $workspaceId) {\n integrations {\n app\n isEnabled\n installUrl\n enabledAt\n target\n }\n }\n }\n ": types.WorkspaceIntegrationsDocument, "\n mutation InstallIntegration($input: InstallIntegrationInput!) {\n installIntegration(input: $input)\n }\n ": types.InstallIntegrationDocument, "\n mutation RemoveIntegration($input: RemoveIntegrationInput!) {\n removeIntegration(input: $input)\n }\n ": types.RemoveIntegrationDocument, + "\n mutation SendTestMessage($input: SendTestMessageInput!) {\n sendTestMessage(input: $input)\n }\n ": types.SendTestMessageDocument, "\n query People($workspaceId: SweetID!, $input: PeopleQueryInput) {\n workspace(workspaceId: $workspaceId) {\n people(input: $input) {\n id\n name\n handle\n avatar\n }\n }\n }\n ": types.PeopleDocument, "\n query Person($workspaceId: SweetID!, $handle: String!) {\n workspace(workspaceId: $workspaceId) {\n person(handle: $handle) {\n id\n name\n handle\n avatar\n teamMemberships {\n id\n role\n team {\n id\n name\n icon\n }\n }\n }\n }\n }\n ": types.PersonDocument, "\n query PersonalMetrics($workspaceId: SweetID!) {\n workspace(workspaceId: $workspaceId) {\n me {\n personalMetrics {\n pullRequestSize {\n current\n previous\n change\n }\n codeReviewAmount {\n current\n previous\n change\n }\n }\n }\n }\n }\n ": types.PersonalMetricsDocument, @@ -146,6 +147,10 @@ export function graphql(source: "\n mutation InstallIntegration($input: * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "\n mutation RemoveIntegration($input: RemoveIntegrationInput!) {\n removeIntegration(input: $input)\n }\n "): (typeof documents)["\n mutation RemoveIntegration($input: RemoveIntegrationInput!) {\n removeIntegration(input: $input)\n }\n "]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n mutation SendTestMessage($input: SendTestMessageInput!) {\n sendTestMessage(input: $input)\n }\n "): (typeof documents)["\n mutation SendTestMessage($input: SendTestMessageInput!) {\n sendTestMessage(input: $input)\n }\n "]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/packages/graphql-types/frontend/graphql.ts b/packages/graphql-types/frontend/graphql.ts index 2e199b6..ecb5372 100644 --- a/packages/graphql-types/frontend/graphql.ts +++ b/packages/graphql-types/frontend/graphql.ts @@ -245,6 +245,7 @@ export type Mutation = { loginToStripe?: Maybe; loginWithGithub: LoginWithGithubResponse; removeIntegration?: Maybe; + sendTestMessage?: Maybe; unarchiveTeam: Team; updateAutomation: Automation; updateDigest: Digest; @@ -278,6 +279,11 @@ export type MutationRemoveIntegrationArgs = { }; +export type MutationSendTestMessageArgs = { + input: SendTestMessageInput; +}; + + export type MutationUnarchiveTeamArgs = { input: UnarchiveTeamInput; }; @@ -513,6 +519,12 @@ export type Repository = { name: Scalars['String']['output']; }; +export type SendTestMessageInput = { + app: IntegrationApp; + channel: Scalars['String']['input']; + workspaceId: Scalars['SweetID']['input']; +}; + export type Subscription = { __typename?: 'Subscription'; isActive: Scalars['Boolean']['output']; @@ -872,6 +884,13 @@ export type RemoveIntegrationMutationVariables = Exact<{ export type RemoveIntegrationMutation = { __typename?: 'Mutation', removeIntegration?: null | null }; +export type SendTestMessageMutationVariables = Exact<{ + input: SendTestMessageInput; +}>; + + +export type SendTestMessageMutation = { __typename?: 'Mutation', sendTestMessage?: null | null }; + export type PeopleQueryVariables = Exact<{ workspaceId: Scalars['SweetID']['input']; input?: InputMaybe; @@ -1018,6 +1037,7 @@ export const UpdateDigestDocument = {"kind":"Document","definitions":[{"kind":"O export const WorkspaceIntegrationsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"WorkspaceIntegrations"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SweetID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"integrations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"app"}},{"kind":"Field","name":{"kind":"Name","value":"isEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"installUrl"}},{"kind":"Field","name":{"kind":"Name","value":"enabledAt"}},{"kind":"Field","name":{"kind":"Name","value":"target"}}]}}]}}]}}]} as unknown as DocumentNode; export const InstallIntegrationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"InstallIntegration"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"InstallIntegrationInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"installIntegration"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode; export const RemoveIntegrationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RemoveIntegration"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoveIntegrationInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"removeIntegration"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode; +export const SendTestMessageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SendTestMessage"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SendTestMessageInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sendTestMessage"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode; export const PeopleDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"People"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SweetID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"PeopleQueryInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"people"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"handle"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}}]}}]}}]} as unknown as DocumentNode; export const PersonDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Person"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SweetID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"handle"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"person"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"handle"},"value":{"kind":"Variable","name":{"kind":"Name","value":"handle"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"handle"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"teamMemberships"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const PersonalMetricsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PersonalMetrics"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SweetID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"me"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"personalMetrics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"pullRequestSize"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"current"}},{"kind":"Field","name":{"kind":"Name","value":"previous"}},{"kind":"Field","name":{"kind":"Name","value":"change"}}]}},{"kind":"Field","name":{"kind":"Name","value":"codeReviewAmount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"current"}},{"kind":"Field","name":{"kind":"Name","value":"previous"}},{"kind":"Field","name":{"kind":"Name","value":"change"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; diff --git a/packages/graphql-types/shared.ts b/packages/graphql-types/shared.ts index 2b3815c..716320d 100644 --- a/packages/graphql-types/shared.ts +++ b/packages/graphql-types/shared.ts @@ -34,6 +34,7 @@ export enum ErrorCode { INPUT_VALIDATION_FAILED = "INPUT_VALIDATION_FAILED", DATA_ACCESS_ERROR = "DATA_ACCESS_ERROR", SUBSCRIPTION_REQUIRED = "SUBSCRIPTION_REQUIRED", + RATE_LIMIT_EXCEEDED = "RATE_LIMIT_EXCEEDED", INTEGRATION_FAILED = "INTEGRATION_FAILED", UNKNOWN = "UNKNOWN", }